From 063d7009163a590f501fa23f579edcbac9634327 Mon Sep 17 00:00:00 2001 From: Tom Yu Date: Wed, 2 Nov 2005 22:30:42 +0000 Subject: [PATCH] pull up r17476 from trunk ticket: 3232 target_version: 1.4.3 version_fixed: 1.4.3 git-svn-id: svn://anonsvn.mit.edu/krb5/branches/krb5-1-4@17479 dc483132-0cff-0310-8789-dd5450dbe970 --- src/windows/Makefile.in | 10 + src/windows/identity/Makefile | 179 + src/windows/identity/config/Makefile | 133 + src/windows/identity/config/Makefile.w32 | 278 ++ src/windows/identity/config/ccsv.pl | 124 + src/windows/identity/config/csvschema.cfg | 67 + src/windows/identity/doc/Makefile | 68 + src/windows/identity/doc/cred_aquisition.h | 208 ++ src/windows/identity/doc/cred_data_types.h | 246 ++ src/windows/identity/doc/cred_main.h | 35 + src/windows/identity/doc/cred_msgs.h | 47 + src/windows/identity/doc/cred_prop_pages.h | 83 + src/windows/identity/doc/doxyfile.cfg | 1000 +++++ src/windows/identity/doc/footer.html | 19 + src/windows/identity/doc/header.html | 5 + src/windows/identity/doc/images/Thumbs.db | Bin 0 -> 28160 bytes .../doc/images/credview-select-outline.jpg | Bin 0 -> 16084 bytes .../identity/doc/images/khimaira_logo.png | Bin 0 -> 3970 bytes .../identity/doc/images/khimaira_logo_old.jpg | Bin 0 -> 9550 bytes .../doc/images/khimaira_logo_small.png | Bin 0 -> 3970 bytes .../doc/images/khimaira_logo_small_old.jpg | Bin 0 -> 1665 bytes src/windows/identity/doc/main_page.h | 108 + src/windows/identity/doc/plugin_framework.h | 131 + src/windows/identity/doc/plugin_locale.h | 107 + src/windows/identity/doc/plugin_main.h | 126 + src/windows/identity/doc/plugin_structure.h | 50 + src/windows/identity/doc/stylesheet.css | 271 ++ src/windows/identity/doc/ui_actions.h | 29 + src/windows/identity/doc/ui_context.h | 187 + src/windows/identity/doc/ui_main.h | 34 + src/windows/identity/doc/ui_menus.h | 29 + src/windows/identity/help/Index.hhk | 9 + src/windows/identity/help/Makefile | 36 + .../identity/help/html/images/Thumbs.db | Bin 0 -> 3584 bytes .../identity/help/html/images/link.GIF | Bin 0 -> 932 bytes src/windows/identity/help/html/khm.css | 13 + src/windows/identity/help/html/menu_exit.htm | 9 + src/windows/identity/help/html/menu_file.htm | 18 + .../identity/help/html/menu_properties.htm | 9 + src/windows/identity/help/html/template.htm | 9 + src/windows/identity/help/html/welcome.htm | 24 + src/windows/identity/help/khhelp.h | 23 + src/windows/identity/help/netidmgr.hhp | 21 + src/windows/identity/help/toc.hhc | 47 + src/windows/identity/include/Makefile | 37 + src/windows/identity/include/khdefs.h | 235 ++ src/windows/identity/include/kherror.h | 177 + src/windows/identity/include/khlist.h | 173 + src/windows/identity/include/khmsgtypes.h | 700 ++++ src/windows/identity/include/khthread.h | 42 + src/windows/identity/kconfig/Makefile | 51 + src/windows/identity/kconfig/api.c | 2098 +++++++++++ src/windows/identity/kconfig/kconfig.h | 823 +++++ .../identity/kconfig/kconfiginternal.h | 114 + src/windows/identity/kconfig/kconfigmain.c | 37 + src/windows/identity/kconfig/registry.c | 28 + src/windows/identity/kconfig/test/utiltest.c | 207 ++ src/windows/identity/kcreddb/Makefile | 52 + src/windows/identity/kcreddb/attrib.c | 853 +++++ src/windows/identity/kcreddb/attrib.h | 55 + src/windows/identity/kcreddb/buf.c | 391 ++ src/windows/identity/kcreddb/buf.h | 78 + src/windows/identity/kcreddb/credential.c | 1047 ++++++ src/windows/identity/kcreddb/credential.h | 70 + src/windows/identity/kcreddb/credset.c | 1132 ++++++ src/windows/identity/kcreddb/credset.h | 75 + src/windows/identity/kcreddb/credtype.c | 411 +++ src/windows/identity/kcreddb/credtype.h | 55 + src/windows/identity/kcreddb/identity.c | 1537 ++++++++ src/windows/identity/kcreddb/identity.h | 62 + src/windows/identity/kcreddb/init.c | 91 + src/windows/identity/kcreddb/kcdbconfig.csv | 15 + src/windows/identity/kcreddb/kcreddb.h | 3212 ++++++++++++++++ .../identity/kcreddb/kcreddbinternal.h | 60 + src/windows/identity/kcreddb/kcreddbmain.c | 40 + .../identity/kcreddb/lang/en_us/kcredres.rc | 130 + src/windows/identity/kcreddb/langres.h | 49 + src/windows/identity/kcreddb/resource.h | 27 + src/windows/identity/kcreddb/type.c | 1295 +++++++ src/windows/identity/kcreddb/type.h | 216 ++ src/windows/identity/kherr/Makefile | 43 + src/windows/identity/kherr/kherr.c | 1164 ++++++ src/windows/identity/kherr/kherr.h | 965 +++++ src/windows/identity/kherr/kherrinternal.h | 67 + src/windows/identity/kherr/kherrmain.c | 52 + src/windows/identity/kmm/Makefile | 54 + src/windows/identity/kmm/kmm.c | 167 + src/windows/identity/kmm/kmm.h | 1010 ++++++ src/windows/identity/kmm/kmm_module.c | 338 ++ src/windows/identity/kmm/kmm_plugin.c | 377 ++ src/windows/identity/kmm/kmm_reg.c | 291 ++ src/windows/identity/kmm/kmm_registrar.c | 836 +++++ src/windows/identity/kmm/kmmconfig.csv | 52 + src/windows/identity/kmm/kmminternal.h | 215 ++ src/windows/identity/kmm/kmmmain.c | 157 + src/windows/identity/kmm/kplugin.h | 146 + src/windows/identity/kmm/lang/kmm_msgs.mc | 146 + src/windows/identity/kmq/Makefile | 48 + src/windows/identity/kmq/consumer.c | 423 +++ src/windows/identity/kmq/init.c | 251 ++ src/windows/identity/kmq/kmq.h | 743 ++++ src/windows/identity/kmq/kmqconfig.csv | 5 + src/windows/identity/kmq/kmqinternal.h | 111 + src/windows/identity/kmq/kmqmain.c | 47 + src/windows/identity/kmq/msgtype.c | 357 ++ src/windows/identity/kmq/publisher.c | 470 +++ src/windows/identity/nidmgrdll/Makefile | 106 + src/windows/identity/nidmgrdll/dllmain.c | 114 + src/windows/identity/nidmgrdll/nidmgrdll.rc | 74 + src/windows/identity/plugins/common/Makefile | 42 + .../identity/plugins/common/dynimport.c | 420 +++ .../identity/plugins/common/dynimport.h | 338 ++ .../identity/plugins/common/krb5common.c | 156 + .../identity/plugins/common/krb5common.h | 43 + src/windows/identity/plugins/krb4/Makefile | 78 + src/windows/identity/plugins/krb4/datarep.h | 37 + .../identity/plugins/krb4/errorfuncs.c | 264 ++ .../identity/plugins/krb4/errorfuncs.h | 80 + .../identity/plugins/krb4/krb4configdlg.c | 88 + src/windows/identity/plugins/krb4/krb4funcs.c | 505 +++ src/windows/identity/plugins/krb4/krb4funcs.h | 190 + .../identity/plugins/krb4/krb4plugin.c | 164 + .../identity/plugins/krb4/krbconfig.csv | 23 + src/windows/identity/plugins/krb4/krbcred.h | 114 + .../plugins/krb4/lang/en_us/langres.rc | 141 + src/windows/identity/plugins/krb4/langres.h | 78 + src/windows/identity/plugins/krb4/main.c | 191 + src/windows/identity/plugins/krb5/Makefile | 91 + src/windows/identity/plugins/krb5/datarep.c | 269 ++ src/windows/identity/plugins/krb5/datarep.h | 37 + .../identity/plugins/krb5/errorfuncs.c | 260 ++ .../identity/plugins/krb5/errorfuncs.h | 75 + .../identity/plugins/krb5/krb5configdlg.c | 421 +++ src/windows/identity/plugins/krb5/krb5funcs.c | 1889 ++++++++++ src/windows/identity/plugins/krb5/krb5funcs.h | 121 + .../identity/plugins/krb5/krb5identpro.c | 1108 ++++++ .../identity/plugins/krb5/krb5newcreds.c | 2167 +++++++++++ .../identity/plugins/krb5/krb5plugin.c | 230 ++ src/windows/identity/plugins/krb5/krb5props.c | 117 + src/windows/identity/plugins/krb5/krb5util.c | 1362 +++++++ .../identity/plugins/krb5/krbconfig.csv | 34 + src/windows/identity/plugins/krb5/krbcred.h | 182 + .../plugins/krb5/lang/en_us/langres.rc | 406 +++ .../identity/plugins/krb5/lang/krb5_msgs.mc | 151 + src/windows/identity/plugins/krb5/langres.h | 127 + src/windows/identity/plugins/krb5/main.c | 387 ++ src/windows/identity/ui/Makefile | 81 + src/windows/identity/ui/aboutwnd.c | 147 + src/windows/identity/ui/aboutwnd.h | 33 + src/windows/identity/ui/appglobal.h | 74 + src/windows/identity/ui/cfg_general_wnd.c | 227 ++ src/windows/identity/ui/cfg_identities_wnd.c | 1256 +++++++ src/windows/identity/ui/cfg_notif_wnd.c | 313 ++ src/windows/identity/ui/cfg_plugins_wnd.c | 318 ++ src/windows/identity/ui/configwnd.c | 839 +++++ src/windows/identity/ui/configwnd.h | 80 + src/windows/identity/ui/credfuncs.c | 827 +++++ src/windows/identity/ui/credfuncs.h | 72 + src/windows/identity/ui/credwnd.c | 3223 +++++++++++++++++ src/windows/identity/ui/credwnd.h | 237 ++ src/windows/identity/ui/htmlwnd.h | 0 src/windows/identity/ui/htwnd.c | 1070 ++++++ src/windows/identity/ui/htwnd.h | 55 + src/windows/identity/ui/images/Thumbs.db | Bin 0 -> 116224 bytes .../identity/ui/images/app_notify_error.ico | Bin 0 -> 25214 bytes .../identity/ui/images/app_notify_info.ico | Bin 0 -> 25214 bytes .../identity/ui/images/app_notify_none.ico | Bin 0 -> 25214 bytes .../identity/ui/images/app_notify_warn.ico | Bin 0 -> 25214 bytes src/windows/identity/ui/images/bitmap1.bmp | Bin 0 -> 1270 bytes .../identity/ui/images/cfg_applied.ico | Bin 0 -> 1406 bytes .../identity/ui/images/cfg_default.ico | Bin 0 -> 1406 bytes .../identity/ui/images/cfg_deleted.ico | Bin 0 -> 1406 bytes src/windows/identity/ui/images/cfg_mod.ico | Bin 0 -> 1406 bytes .../identity/ui/images/chpw-dis-sm.bmp | Bin 0 -> 822 bytes src/windows/identity/ui/images/chpw-dis.bmp | Bin 0 -> 2430 bytes src/windows/identity/ui/images/chpw-sm.bmp | Bin 0 -> 822 bytes src/windows/identity/ui/images/chpw.bmp | Bin 0 -> 2430 bytes src/windows/identity/ui/images/disabled.ico | Bin 0 -> 2166 bytes src/windows/identity/ui/images/enabled.ico | Bin 0 -> 2166 bytes .../identity/ui/images/flag-critical.bmp | Bin 0 -> 1014 bytes .../identity/ui/images/flag-warning.bmp | Bin 0 -> 1014 bytes .../identity/ui/images/flag_expired.bmp | Bin 0 -> 1014 bytes src/windows/identity/ui/images/help-sm.bmp | Bin 0 -> 1014 bytes src/windows/identity/ui/images/help.bmp | Bin 0 -> 2430 bytes src/windows/identity/ui/images/icon1.ico | Bin 0 -> 766 bytes .../identity/ui/images/id-delete-dis-sm.bmp | Bin 0 -> 1014 bytes .../identity/ui/images/id-delete-dis.bmp | Bin 0 -> 3186 bytes .../identity/ui/images/id-delete-sm.bmp | Bin 0 -> 1014 bytes src/windows/identity/ui/images/id-delete.bmp | Bin 0 -> 3186 bytes src/windows/identity/ui/images/id-dis-sm.bmp | Bin 0 -> 1014 bytes src/windows/identity/ui/images/id-dis.bmp | Bin 0 -> 3186 bytes .../identity/ui/images/id-new-dis-sm.bmp | Bin 0 -> 1014 bytes src/windows/identity/ui/images/id-new-dis.bmp | Bin 0 -> 3186 bytes src/windows/identity/ui/images/id-new-sm.bmp | Bin 0 -> 1014 bytes src/windows/identity/ui/images/id-new.bmp | Bin 0 -> 3186 bytes .../identity/ui/images/id-refresh-dis.bmp | Bin 0 -> 3186 bytes .../identity/ui/images/id-refresh-sm-dis.bmp | Bin 0 -> 1014 bytes .../identity/ui/images/id-refresh-sm.bmp | Bin 0 -> 1014 bytes src/windows/identity/ui/images/id-refresh.bmp | Bin 0 -> 3186 bytes src/windows/identity/ui/images/id-sm.bmp | Bin 0 -> 822 bytes src/windows/identity/ui/images/id.bmp | Bin 0 -> 3186 bytes src/windows/identity/ui/images/id.ico | Bin 0 -> 2166 bytes src/windows/identity/ui/images/ident.png | Bin 0 -> 423 bytes src/windows/identity/ui/images/import-dis.bmp | Bin 0 -> 2430 bytes .../identity/ui/images/import-sm-dis.bmp | Bin 0 -> 774 bytes src/windows/identity/ui/images/import-sm.bmp | Bin 0 -> 774 bytes src/windows/identity/ui/images/import.bmp | Bin 0 -> 2430 bytes .../identity/ui/images/khimaira-cfg.bmp | Bin 0 -> 30056 bytes src/windows/identity/ui/images/logo_shade.bmp | Bin 0 -> 30056 bytes src/windows/identity/ui/images/main_app.ico | Bin 0 -> 25214 bytes .../identity/ui/images/main_app_old.ico | Bin 0 -> 7854 bytes .../identity/ui/images/tb-blank-small.bmp | Bin 0 -> 774 bytes src/windows/identity/ui/images/tb-blank.bmp | Bin 0 -> 2430 bytes src/windows/identity/ui/images/tb-space.bmp | Bin 0 -> 2430 bytes src/windows/identity/ui/images/text1138.png | Bin 0 -> 378 bytes .../identity/ui/images/tk-delete-dis-sm.bmp | Bin 0 -> 1014 bytes .../identity/ui/images/tk-delete-dis.bmp | Bin 0 -> 3186 bytes .../identity/ui/images/tk-delete-sm.bmp | Bin 0 -> 822 bytes src/windows/identity/ui/images/tk-delete.bmp | Bin 0 -> 2430 bytes src/windows/identity/ui/images/tk-dis-sm.bmp | Bin 0 -> 1014 bytes src/windows/identity/ui/images/tk-dis.bmp | Bin 0 -> 3186 bytes .../identity/ui/images/tk-new-dis-sm.bmp | Bin 0 -> 1014 bytes src/windows/identity/ui/images/tk-new-dis.bmp | Bin 0 -> 3186 bytes src/windows/identity/ui/images/tk-new-sm.bmp | Bin 0 -> 822 bytes src/windows/identity/ui/images/tk-new.bmp | Bin 0 -> 2430 bytes .../identity/ui/images/tk-refresh-dis-sm.bmp | Bin 0 -> 1014 bytes .../identity/ui/images/tk-refresh-dis.bmp | Bin 0 -> 3186 bytes .../identity/ui/images/tk-refresh-sm.bmp | Bin 0 -> 822 bytes src/windows/identity/ui/images/tk-refresh.bmp | Bin 0 -> 2430 bytes src/windows/identity/ui/images/tk-sm.bmp | Bin 0 -> 1014 bytes src/windows/identity/ui/images/tk.bmp | Bin 0 -> 3186 bytes .../identity/ui/images/vw-refresh-sm.bmp | Bin 0 -> 822 bytes src/windows/identity/ui/images/vw-refresh.bmp | Bin 0 -> 2430 bytes .../identity/ui/images/wdg_collapsed.bmp | Bin 0 -> 1014 bytes .../identity/ui/images/wdg_collapsed_hi.bmp | Bin 0 -> 1014 bytes .../identity/ui/images/wdg_credtype.bmp | Bin 0 -> 1014 bytes .../identity/ui/images/wdg_expanded.bmp | Bin 0 -> 1014 bytes .../identity/ui/images/wdg_expanded_hi.bmp | Bin 0 -> 1014 bytes src/windows/identity/ui/images/wdg_flag.bmp | Bin 0 -> 1014 bytes .../identity/ui/images/wgt_arrow_collapse.ico | Bin 0 -> 1406 bytes .../identity/ui/images/wgt_arrow_expand.ico | Bin 0 -> 1406 bytes src/windows/identity/ui/khmapp.h | 69 + src/windows/identity/ui/lang/en_us/khapp.rc | 728 ++++ src/windows/identity/ui/main.c | 442 +++ src/windows/identity/ui/mainmenu.c | 566 +++ src/windows/identity/ui/mainmenu.h | 58 + src/windows/identity/ui/mainwnd.c | 679 ++++ src/windows/identity/ui/mainwnd.h | 60 + src/windows/identity/ui/makeacceldef.pl | 29 + src/windows/identity/ui/makeactiondef.pl | 29 + .../identity/ui/netidmgr.exe.manifest.i386 | 22 + .../identity/ui/netidmgr.manifest.i386.vc7 | 22 + .../ui/netidmgr.manifest.i386.vc7.debug | 22 + .../identity/ui/netidmgr.manifest.i386.vc8 | 31 + .../ui/netidmgr.manifest.i386.vc8.debug | 31 + src/windows/identity/ui/newcredwnd.c | 1694 +++++++++ src/windows/identity/ui/newcredwnd.h | 101 + src/windows/identity/ui/notifier.c | 1079 ++++++ src/windows/identity/ui/notifier.h | 45 + src/windows/identity/ui/passwnd.c | 133 + src/windows/identity/ui/passwnd.h | 39 + src/windows/identity/ui/propertywnd.c | 248 ++ src/windows/identity/ui/propertywnd.h | 36 + src/windows/identity/ui/reqdaemon.c | 396 ++ src/windows/identity/ui/reqdaemon.h | 42 + src/windows/identity/ui/resource.h | 313 ++ src/windows/identity/ui/statusbar.c | 149 + src/windows/identity/ui/statusbar.h | 53 + src/windows/identity/ui/timer.c | 637 ++++ src/windows/identity/ui/timer.h | 100 + src/windows/identity/ui/toolbar.c | 366 ++ src/windows/identity/ui/toolbar.h | 52 + src/windows/identity/ui/uiconfig.csv | 111 + src/windows/identity/uilib/Makefile | 61 + src/windows/identity/uilib/accel.csv | 17 + src/windows/identity/uilib/acceldef.cfg | 50 + src/windows/identity/uilib/action.c | 1019 ++++++ src/windows/identity/uilib/actiondef.cfg | 64 + src/windows/identity/uilib/actions.csv | 37 + src/windows/identity/uilib/alert.c | 350 ++ src/windows/identity/uilib/configui.c | 1001 +++++ src/windows/identity/uilib/configui.h | 74 + src/windows/identity/uilib/creddlg.c | 671 ++++ src/windows/identity/uilib/khaction.h | 566 +++ src/windows/identity/uilib/khactiondef.h | 134 + src/windows/identity/uilib/khalerts.h | 372 ++ src/windows/identity/uilib/khconfigui.h | 588 +++ src/windows/identity/uilib/khhtlink.h | 58 + src/windows/identity/uilib/khnewcred.h | 896 +++++ src/windows/identity/uilib/khprops.h | 202 ++ src/windows/identity/uilib/khremote.h | 84 + src/windows/identity/uilib/khrescache.h | 100 + src/windows/identity/uilib/khtracker.h | 113 + src/windows/identity/uilib/khuidefs.h | 58 + src/windows/identity/uilib/propsheet.c | 188 + src/windows/identity/uilib/propwnd.c | 37 + src/windows/identity/uilib/rescache.c | 301 ++ src/windows/identity/uilib/trackerwnd.c | 453 +++ src/windows/identity/uilib/uilibmain.c | 41 + src/windows/identity/util/Makefile | 46 + src/windows/identity/util/hashtable.c | 167 + src/windows/identity/util/hashtable.h | 223 ++ src/windows/identity/util/mstring.c | 516 +++ src/windows/identity/util/mstring.h | 361 ++ src/windows/identity/util/sync.c | 121 + src/windows/identity/util/sync.h | 128 + src/windows/identity/util/utils.h | 36 + 307 files changed, 67049 insertions(+) create mode 100644 src/windows/identity/Makefile create mode 100644 src/windows/identity/config/Makefile create mode 100644 src/windows/identity/config/Makefile.w32 create mode 100644 src/windows/identity/config/ccsv.pl create mode 100644 src/windows/identity/config/csvschema.cfg create mode 100644 src/windows/identity/doc/Makefile create mode 100644 src/windows/identity/doc/cred_aquisition.h create mode 100644 src/windows/identity/doc/cred_data_types.h create mode 100644 src/windows/identity/doc/cred_main.h create mode 100644 src/windows/identity/doc/cred_msgs.h create mode 100644 src/windows/identity/doc/cred_prop_pages.h create mode 100644 src/windows/identity/doc/doxyfile.cfg create mode 100644 src/windows/identity/doc/footer.html create mode 100644 src/windows/identity/doc/header.html create mode 100644 src/windows/identity/doc/images/Thumbs.db create mode 100644 src/windows/identity/doc/images/credview-select-outline.jpg create mode 100644 src/windows/identity/doc/images/khimaira_logo.png create mode 100644 src/windows/identity/doc/images/khimaira_logo_old.jpg create mode 100644 src/windows/identity/doc/images/khimaira_logo_small.png create mode 100644 src/windows/identity/doc/images/khimaira_logo_small_old.jpg create mode 100644 src/windows/identity/doc/main_page.h create mode 100644 src/windows/identity/doc/plugin_framework.h create mode 100644 src/windows/identity/doc/plugin_locale.h create mode 100644 src/windows/identity/doc/plugin_main.h create mode 100644 src/windows/identity/doc/plugin_structure.h create mode 100644 src/windows/identity/doc/stylesheet.css create mode 100644 src/windows/identity/doc/ui_actions.h create mode 100644 src/windows/identity/doc/ui_context.h create mode 100644 src/windows/identity/doc/ui_main.h create mode 100644 src/windows/identity/doc/ui_menus.h create mode 100644 src/windows/identity/help/Index.hhk create mode 100644 src/windows/identity/help/Makefile create mode 100644 src/windows/identity/help/html/images/Thumbs.db create mode 100644 src/windows/identity/help/html/images/link.GIF create mode 100644 src/windows/identity/help/html/khm.css create mode 100644 src/windows/identity/help/html/menu_exit.htm create mode 100644 src/windows/identity/help/html/menu_file.htm create mode 100644 src/windows/identity/help/html/menu_properties.htm create mode 100644 src/windows/identity/help/html/template.htm create mode 100644 src/windows/identity/help/html/welcome.htm create mode 100644 src/windows/identity/help/khhelp.h create mode 100644 src/windows/identity/help/netidmgr.hhp create mode 100644 src/windows/identity/help/toc.hhc create mode 100644 src/windows/identity/include/Makefile create mode 100644 src/windows/identity/include/khdefs.h create mode 100644 src/windows/identity/include/kherror.h create mode 100644 src/windows/identity/include/khlist.h create mode 100644 src/windows/identity/include/khmsgtypes.h create mode 100644 src/windows/identity/include/khthread.h create mode 100644 src/windows/identity/kconfig/Makefile create mode 100644 src/windows/identity/kconfig/api.c create mode 100644 src/windows/identity/kconfig/kconfig.h create mode 100644 src/windows/identity/kconfig/kconfiginternal.h create mode 100644 src/windows/identity/kconfig/kconfigmain.c create mode 100644 src/windows/identity/kconfig/registry.c create mode 100644 src/windows/identity/kconfig/test/utiltest.c create mode 100644 src/windows/identity/kcreddb/Makefile create mode 100644 src/windows/identity/kcreddb/attrib.c create mode 100644 src/windows/identity/kcreddb/attrib.h create mode 100644 src/windows/identity/kcreddb/buf.c create mode 100644 src/windows/identity/kcreddb/buf.h create mode 100644 src/windows/identity/kcreddb/credential.c create mode 100644 src/windows/identity/kcreddb/credential.h create mode 100644 src/windows/identity/kcreddb/credset.c create mode 100644 src/windows/identity/kcreddb/credset.h create mode 100644 src/windows/identity/kcreddb/credtype.c create mode 100644 src/windows/identity/kcreddb/credtype.h create mode 100644 src/windows/identity/kcreddb/identity.c create mode 100644 src/windows/identity/kcreddb/identity.h create mode 100644 src/windows/identity/kcreddb/init.c create mode 100644 src/windows/identity/kcreddb/kcdbconfig.csv create mode 100644 src/windows/identity/kcreddb/kcreddb.h create mode 100644 src/windows/identity/kcreddb/kcreddbinternal.h create mode 100644 src/windows/identity/kcreddb/kcreddbmain.c create mode 100644 src/windows/identity/kcreddb/lang/en_us/kcredres.rc create mode 100644 src/windows/identity/kcreddb/langres.h create mode 100644 src/windows/identity/kcreddb/resource.h create mode 100644 src/windows/identity/kcreddb/type.c create mode 100644 src/windows/identity/kcreddb/type.h create mode 100644 src/windows/identity/kherr/Makefile create mode 100644 src/windows/identity/kherr/kherr.c create mode 100644 src/windows/identity/kherr/kherr.h create mode 100644 src/windows/identity/kherr/kherrinternal.h create mode 100644 src/windows/identity/kherr/kherrmain.c create mode 100644 src/windows/identity/kmm/Makefile create mode 100644 src/windows/identity/kmm/kmm.c create mode 100644 src/windows/identity/kmm/kmm.h create mode 100644 src/windows/identity/kmm/kmm_module.c create mode 100644 src/windows/identity/kmm/kmm_plugin.c create mode 100644 src/windows/identity/kmm/kmm_reg.c create mode 100644 src/windows/identity/kmm/kmm_registrar.c create mode 100644 src/windows/identity/kmm/kmmconfig.csv create mode 100644 src/windows/identity/kmm/kmminternal.h create mode 100644 src/windows/identity/kmm/kmmmain.c create mode 100644 src/windows/identity/kmm/kplugin.h create mode 100644 src/windows/identity/kmm/lang/kmm_msgs.mc create mode 100644 src/windows/identity/kmq/Makefile create mode 100644 src/windows/identity/kmq/consumer.c create mode 100644 src/windows/identity/kmq/init.c create mode 100644 src/windows/identity/kmq/kmq.h create mode 100644 src/windows/identity/kmq/kmqconfig.csv create mode 100644 src/windows/identity/kmq/kmqinternal.h create mode 100644 src/windows/identity/kmq/kmqmain.c create mode 100644 src/windows/identity/kmq/msgtype.c create mode 100644 src/windows/identity/kmq/publisher.c create mode 100644 src/windows/identity/nidmgrdll/Makefile create mode 100644 src/windows/identity/nidmgrdll/dllmain.c create mode 100644 src/windows/identity/nidmgrdll/nidmgrdll.rc create mode 100644 src/windows/identity/plugins/common/Makefile create mode 100644 src/windows/identity/plugins/common/dynimport.c create mode 100644 src/windows/identity/plugins/common/dynimport.h create mode 100644 src/windows/identity/plugins/common/krb5common.c create mode 100644 src/windows/identity/plugins/common/krb5common.h create mode 100644 src/windows/identity/plugins/krb4/Makefile create mode 100644 src/windows/identity/plugins/krb4/datarep.h create mode 100644 src/windows/identity/plugins/krb4/errorfuncs.c create mode 100644 src/windows/identity/plugins/krb4/errorfuncs.h create mode 100644 src/windows/identity/plugins/krb4/krb4configdlg.c create mode 100644 src/windows/identity/plugins/krb4/krb4funcs.c create mode 100644 src/windows/identity/plugins/krb4/krb4funcs.h create mode 100644 src/windows/identity/plugins/krb4/krb4plugin.c create mode 100644 src/windows/identity/plugins/krb4/krbconfig.csv create mode 100644 src/windows/identity/plugins/krb4/krbcred.h create mode 100644 src/windows/identity/plugins/krb4/lang/en_us/langres.rc create mode 100644 src/windows/identity/plugins/krb4/langres.h create mode 100644 src/windows/identity/plugins/krb4/main.c create mode 100644 src/windows/identity/plugins/krb5/Makefile create mode 100644 src/windows/identity/plugins/krb5/datarep.c create mode 100644 src/windows/identity/plugins/krb5/datarep.h create mode 100644 src/windows/identity/plugins/krb5/errorfuncs.c create mode 100644 src/windows/identity/plugins/krb5/errorfuncs.h create mode 100644 src/windows/identity/plugins/krb5/krb5configdlg.c create mode 100644 src/windows/identity/plugins/krb5/krb5funcs.c create mode 100644 src/windows/identity/plugins/krb5/krb5funcs.h create mode 100644 src/windows/identity/plugins/krb5/krb5identpro.c create mode 100644 src/windows/identity/plugins/krb5/krb5newcreds.c create mode 100644 src/windows/identity/plugins/krb5/krb5plugin.c create mode 100644 src/windows/identity/plugins/krb5/krb5props.c create mode 100644 src/windows/identity/plugins/krb5/krb5util.c create mode 100644 src/windows/identity/plugins/krb5/krbconfig.csv create mode 100644 src/windows/identity/plugins/krb5/krbcred.h create mode 100644 src/windows/identity/plugins/krb5/lang/en_us/langres.rc create mode 100644 src/windows/identity/plugins/krb5/lang/krb5_msgs.mc create mode 100644 src/windows/identity/plugins/krb5/langres.h create mode 100644 src/windows/identity/plugins/krb5/main.c create mode 100644 src/windows/identity/ui/Makefile create mode 100644 src/windows/identity/ui/aboutwnd.c create mode 100644 src/windows/identity/ui/aboutwnd.h create mode 100644 src/windows/identity/ui/appglobal.h create mode 100644 src/windows/identity/ui/cfg_general_wnd.c create mode 100644 src/windows/identity/ui/cfg_identities_wnd.c create mode 100644 src/windows/identity/ui/cfg_notif_wnd.c create mode 100644 src/windows/identity/ui/cfg_plugins_wnd.c create mode 100644 src/windows/identity/ui/configwnd.c create mode 100644 src/windows/identity/ui/configwnd.h create mode 100644 src/windows/identity/ui/credfuncs.c create mode 100644 src/windows/identity/ui/credfuncs.h create mode 100644 src/windows/identity/ui/credwnd.c create mode 100644 src/windows/identity/ui/credwnd.h create mode 100644 src/windows/identity/ui/htmlwnd.h create mode 100644 src/windows/identity/ui/htwnd.c create mode 100644 src/windows/identity/ui/htwnd.h create mode 100644 src/windows/identity/ui/images/Thumbs.db create mode 100644 src/windows/identity/ui/images/app_notify_error.ico create mode 100644 src/windows/identity/ui/images/app_notify_info.ico create mode 100644 src/windows/identity/ui/images/app_notify_none.ico create mode 100644 src/windows/identity/ui/images/app_notify_warn.ico create mode 100644 src/windows/identity/ui/images/bitmap1.bmp create mode 100644 src/windows/identity/ui/images/cfg_applied.ico create mode 100644 src/windows/identity/ui/images/cfg_default.ico create mode 100644 src/windows/identity/ui/images/cfg_deleted.ico create mode 100644 src/windows/identity/ui/images/cfg_mod.ico create mode 100644 src/windows/identity/ui/images/chpw-dis-sm.bmp create mode 100644 src/windows/identity/ui/images/chpw-dis.bmp create mode 100644 src/windows/identity/ui/images/chpw-sm.bmp create mode 100644 src/windows/identity/ui/images/chpw.bmp create mode 100644 src/windows/identity/ui/images/disabled.ico create mode 100644 src/windows/identity/ui/images/enabled.ico create mode 100644 src/windows/identity/ui/images/flag-critical.bmp create mode 100644 src/windows/identity/ui/images/flag-warning.bmp create mode 100644 src/windows/identity/ui/images/flag_expired.bmp create mode 100644 src/windows/identity/ui/images/help-sm.bmp create mode 100644 src/windows/identity/ui/images/help.bmp create mode 100644 src/windows/identity/ui/images/icon1.ico create mode 100644 src/windows/identity/ui/images/id-delete-dis-sm.bmp create mode 100644 src/windows/identity/ui/images/id-delete-dis.bmp create mode 100644 src/windows/identity/ui/images/id-delete-sm.bmp create mode 100644 src/windows/identity/ui/images/id-delete.bmp create mode 100644 src/windows/identity/ui/images/id-dis-sm.bmp create mode 100644 src/windows/identity/ui/images/id-dis.bmp create mode 100644 src/windows/identity/ui/images/id-new-dis-sm.bmp create mode 100644 src/windows/identity/ui/images/id-new-dis.bmp create mode 100644 src/windows/identity/ui/images/id-new-sm.bmp create mode 100644 src/windows/identity/ui/images/id-new.bmp create mode 100644 src/windows/identity/ui/images/id-refresh-dis.bmp create mode 100644 src/windows/identity/ui/images/id-refresh-sm-dis.bmp create mode 100644 src/windows/identity/ui/images/id-refresh-sm.bmp create mode 100644 src/windows/identity/ui/images/id-refresh.bmp create mode 100644 src/windows/identity/ui/images/id-sm.bmp create mode 100644 src/windows/identity/ui/images/id.bmp create mode 100644 src/windows/identity/ui/images/id.ico create mode 100644 src/windows/identity/ui/images/ident.png create mode 100644 src/windows/identity/ui/images/import-dis.bmp create mode 100644 src/windows/identity/ui/images/import-sm-dis.bmp create mode 100644 src/windows/identity/ui/images/import-sm.bmp create mode 100644 src/windows/identity/ui/images/import.bmp create mode 100644 src/windows/identity/ui/images/khimaira-cfg.bmp create mode 100644 src/windows/identity/ui/images/logo_shade.bmp create mode 100644 src/windows/identity/ui/images/main_app.ico create mode 100644 src/windows/identity/ui/images/main_app_old.ico create mode 100644 src/windows/identity/ui/images/tb-blank-small.bmp create mode 100644 src/windows/identity/ui/images/tb-blank.bmp create mode 100644 src/windows/identity/ui/images/tb-space.bmp create mode 100644 src/windows/identity/ui/images/text1138.png create mode 100644 src/windows/identity/ui/images/tk-delete-dis-sm.bmp create mode 100644 src/windows/identity/ui/images/tk-delete-dis.bmp create mode 100644 src/windows/identity/ui/images/tk-delete-sm.bmp create mode 100644 src/windows/identity/ui/images/tk-delete.bmp create mode 100644 src/windows/identity/ui/images/tk-dis-sm.bmp create mode 100644 src/windows/identity/ui/images/tk-dis.bmp create mode 100644 src/windows/identity/ui/images/tk-new-dis-sm.bmp create mode 100644 src/windows/identity/ui/images/tk-new-dis.bmp create mode 100644 src/windows/identity/ui/images/tk-new-sm.bmp create mode 100644 src/windows/identity/ui/images/tk-new.bmp create mode 100644 src/windows/identity/ui/images/tk-refresh-dis-sm.bmp create mode 100644 src/windows/identity/ui/images/tk-refresh-dis.bmp create mode 100644 src/windows/identity/ui/images/tk-refresh-sm.bmp create mode 100644 src/windows/identity/ui/images/tk-refresh.bmp create mode 100644 src/windows/identity/ui/images/tk-sm.bmp create mode 100644 src/windows/identity/ui/images/tk.bmp create mode 100644 src/windows/identity/ui/images/vw-refresh-sm.bmp create mode 100644 src/windows/identity/ui/images/vw-refresh.bmp create mode 100644 src/windows/identity/ui/images/wdg_collapsed.bmp create mode 100644 src/windows/identity/ui/images/wdg_collapsed_hi.bmp create mode 100644 src/windows/identity/ui/images/wdg_credtype.bmp create mode 100644 src/windows/identity/ui/images/wdg_expanded.bmp create mode 100644 src/windows/identity/ui/images/wdg_expanded_hi.bmp create mode 100644 src/windows/identity/ui/images/wdg_flag.bmp create mode 100644 src/windows/identity/ui/images/wgt_arrow_collapse.ico create mode 100644 src/windows/identity/ui/images/wgt_arrow_expand.ico create mode 100644 src/windows/identity/ui/khmapp.h create mode 100644 src/windows/identity/ui/lang/en_us/khapp.rc create mode 100644 src/windows/identity/ui/main.c create mode 100644 src/windows/identity/ui/mainmenu.c create mode 100644 src/windows/identity/ui/mainmenu.h create mode 100644 src/windows/identity/ui/mainwnd.c create mode 100644 src/windows/identity/ui/mainwnd.h create mode 100644 src/windows/identity/ui/makeacceldef.pl create mode 100644 src/windows/identity/ui/makeactiondef.pl create mode 100644 src/windows/identity/ui/netidmgr.exe.manifest.i386 create mode 100644 src/windows/identity/ui/netidmgr.manifest.i386.vc7 create mode 100644 src/windows/identity/ui/netidmgr.manifest.i386.vc7.debug create mode 100644 src/windows/identity/ui/netidmgr.manifest.i386.vc8 create mode 100644 src/windows/identity/ui/netidmgr.manifest.i386.vc8.debug create mode 100644 src/windows/identity/ui/newcredwnd.c create mode 100644 src/windows/identity/ui/newcredwnd.h create mode 100644 src/windows/identity/ui/notifier.c create mode 100644 src/windows/identity/ui/notifier.h create mode 100644 src/windows/identity/ui/passwnd.c create mode 100644 src/windows/identity/ui/passwnd.h create mode 100644 src/windows/identity/ui/propertywnd.c create mode 100644 src/windows/identity/ui/propertywnd.h create mode 100644 src/windows/identity/ui/reqdaemon.c create mode 100644 src/windows/identity/ui/reqdaemon.h create mode 100644 src/windows/identity/ui/resource.h create mode 100644 src/windows/identity/ui/statusbar.c create mode 100644 src/windows/identity/ui/statusbar.h create mode 100644 src/windows/identity/ui/timer.c create mode 100644 src/windows/identity/ui/timer.h create mode 100644 src/windows/identity/ui/toolbar.c create mode 100644 src/windows/identity/ui/toolbar.h create mode 100644 src/windows/identity/ui/uiconfig.csv create mode 100644 src/windows/identity/uilib/Makefile create mode 100644 src/windows/identity/uilib/accel.csv create mode 100644 src/windows/identity/uilib/acceldef.cfg create mode 100644 src/windows/identity/uilib/action.c create mode 100644 src/windows/identity/uilib/actiondef.cfg create mode 100644 src/windows/identity/uilib/actions.csv create mode 100644 src/windows/identity/uilib/alert.c create mode 100644 src/windows/identity/uilib/configui.c create mode 100644 src/windows/identity/uilib/configui.h create mode 100644 src/windows/identity/uilib/creddlg.c create mode 100644 src/windows/identity/uilib/khaction.h create mode 100644 src/windows/identity/uilib/khactiondef.h create mode 100644 src/windows/identity/uilib/khalerts.h create mode 100644 src/windows/identity/uilib/khconfigui.h create mode 100644 src/windows/identity/uilib/khhtlink.h create mode 100644 src/windows/identity/uilib/khnewcred.h create mode 100644 src/windows/identity/uilib/khprops.h create mode 100644 src/windows/identity/uilib/khremote.h create mode 100644 src/windows/identity/uilib/khrescache.h create mode 100644 src/windows/identity/uilib/khtracker.h create mode 100644 src/windows/identity/uilib/khuidefs.h create mode 100644 src/windows/identity/uilib/propsheet.c create mode 100644 src/windows/identity/uilib/propwnd.c create mode 100644 src/windows/identity/uilib/rescache.c create mode 100644 src/windows/identity/uilib/trackerwnd.c create mode 100644 src/windows/identity/uilib/uilibmain.c create mode 100644 src/windows/identity/util/Makefile create mode 100644 src/windows/identity/util/hashtable.c create mode 100644 src/windows/identity/util/hashtable.h create mode 100644 src/windows/identity/util/mstring.c create mode 100644 src/windows/identity/util/mstring.h create mode 100644 src/windows/identity/util/sync.c create mode 100644 src/windows/identity/util/sync.h create mode 100644 src/windows/identity/util/utils.h diff --git a/src/windows/Makefile.in b/src/windows/Makefile.in index ebfc6e3a7..ae3c48f12 100644 --- a/src/windows/Makefile.in +++ b/src/windows/Makefile.in @@ -23,6 +23,11 @@ all-windows:: @echo Making in windows\ms2mit cd ..\ms2mit $(MAKE) -$(MFLAGS) +!if "$(KRB5_KFW_COMPILE)"=="1" + @echo Making in windows\identity + cd ..\identity + $(MAKE) -$(MFLAGS) +!endif cd .. clean-windows:: @@ -44,4 +49,9 @@ clean-windows:: @echo Making clean in windows\ms2mit cd ..\ms2mit $(MAKE) -$(MFLAGS) clean +!if "$(KRB5_KFW_COMPILE)"=="1" + @echo Making clean in windows\identity + cd ..\identity + $(MAKE) -$(MFLAGS) clean +!endif cd .. diff --git a/src/windows/identity/Makefile b/src/windows/identity/Makefile new file mode 100644 index 000000000..3a79ee5f5 --- /dev/null +++ b/src/windows/identity/Makefile @@ -0,0 +1,179 @@ +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +!ifdef ETAGRUN +all: finale doc +!else +all: finale +!endif + +MODULE=all +!include + +!ifndef CLEANRUN +!ifndef TESTRUN +!ifndef ETAGRUN +RMAKE=$(MAKECMD) /nologo all +!else +RMAKE=$(MAKECMD) /nologo etag +!endif +!else +RMAKE=$(MAKECMD) /nologo test +!endif +!else +RMAKE=$(MAKECMD) /nologo clean +!endif + +start: + +config: start + $(ECHO) Entering $@: + $(CD) $@ + $(RMAKE) + $(CD) .. + $(ECHO) Done with $@ + +include: config + $(ECHO) Entering $@: + $(CD) $@ + $(RMAKE) + $(CD) .. + $(ECHO) Done with $@ + +util: include + $(ECHO) Entering $@: + $(CD) $@ + $(RMAKE) + $(CD) .. + $(ECHO) Done with $@ + +kherr: util + $(ECHO) Entering $@: + $(CD) $@ + $(RMAKE) + $(CD) .. + $(ECHO) Done with $@ + +kconfig: kherr + $(ECHO) Entering $@: + $(CD) $@ + $(RMAKE) + $(CD) .. + $(ECHO) Done with $@ + +kmq: kconfig + $(ECHO) Entering $@: + $(CD) $@ + $(RMAKE) + $(CD) .. + $(ECHO) Done with $@ + +kcreddb: kmq + $(ECHO) Entering $@: + $(CD) $@ + $(RMAKE) + $(CD) .. + $(ECHO) Done with $@ + +kmm: kcreddb + $(ECHO) Entering $@: + $(CD) $@ + $(RMAKE) + $(CD) .. + $(ECHO) Done with $@ + +help: kmm + $(ECHO) Entering $@: + $(CD) $@ + $(RMAKE) + $(CD) .. + $(ECHO) Done with $@ + +uilib: help + $(ECHO) Entering $@: + $(CD) $@ + $(RMAKE) + $(CD) .. + $(ECHO) Done with $@ + +nidmgrdll: uilib + $(ECHO) Entering $@ + $(CD) $@ + $(RMAKE) + $(CD) .. + $(ECHO) Done with $@ + +ui: nidmgrdll + $(ECHO) Entering $@: + $(CD) $@ + $(RMAKE) + $(CD) .. + $(ECHO) Done with $@ + +# Now build the plugins +plugincommon: ui + $(ECHO) Entering $@ + $(CD) plugins\common + $(RMAKE) + $(CD) ..\.. + $(ECHO) Done with $@ + +krb5plugin: plugincommon + $(ECHO) Entering $@ + $(CD) plugins\krb5 + $(RMAKE) + $(CD) ..\.. + $(ECHO) Done with $@ + +!ifndef NO_KRB4 +finale: krb4plugin + +krb4plugin: plugincommon + $(ECHO) Entering $@ + $(CD) plugins\krb4 + $(RMAKE) + $(CD) ..\.. + $(ECHO) Done with $@ +!endif + +finale: krb5plugin + $(ECHO) Done. + +pdoc: + +doc: pdoc + $(ECHO) Entering $@: + $(CD) $@ + $(RMAKE) + $(CD) .. + $(ECHO) Done with $@ + +clean:: + $(MAKECMD) /nologo CLEANRUN=1 + +test:: + $(MAKECMD) /nologo TESTRUN=1 + +etags:: + $(RM) $(TAGFILE) + $(MAKECMD) /nologo ETAGRUN=1 diff --git a/src/windows/identity/config/Makefile b/src/windows/identity/config/Makefile new file mode 100644 index 000000000..686a044ce --- /dev/null +++ b/src/windows/identity/config/Makefile @@ -0,0 +1,133 @@ +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +MODULE=config +!include + +all: mkalldirs mkversion + +mkalldirs: +! if !exist($(DESTROOT)) + -$(MKDIR) $(DESTROOT) +! endif +! if !exist($(OBJROOT)) + -$(MKDIR) $(OBJROOT) +! endif +! if !exist($(DESTDIR)) + -$(MKDIR) $(DESTDIR) +! endif +! if !exist($(OBJDIR)) + -$(MKDIR) $(OBJDIR) +! endif +! if !exist($(INCDIR)) + -$(MKDIR) $(INCDIR) +! endif +! if !exist($(BINDIR)) + -$(MKDIR) $(BINDIR) +! endif +! if !exist($(LIBDIR)) + -$(MKDIR) $(LIBDIR) +! endif +! if !exist($(DOCDIR)) + -$(MKDIR) $(DOCDIR) +! endif + $(ECHO) Done creating directories. + +VERSIONINC=$(INCDIR)\khimaira_version.h + +# Version related defines + +! if "$(KH_BUILD)"=="RETAIL" +kh_fileflags=0 +! else +kh_fileflags=VS_FF_DEBUG +! endif +! if "$(KH_RELEASE)"=="PRERELEASE" +kh_fileflags=$(kh_fileflags) | VS_FF_PRERELEASE +! elseif "$(KH_RELEASE)"=="PRIVATE" +kh_fileflags=$(kh_fileflags) | VS_FF_PRIVATEBUILD +! elseif "$(KH_RELEASE)"=="SPECIAL" +kh_fileflags=$(kh_fileflags) | VS_FF_SPECIALBUILD +! endif + +kh_fileos=VOS_NT_WINDOWS32 +kh_filetype_app=VFT_APP +kh_filetype_dll=VFT_DLL + +mkversion: $(VERSIONINC) + +$(VERSIONINC): Makefile + $(CP) << $(VERSIONINC) +/* + * This is an autogenerated file. Do not modify directly. + * + * File generated by running $(MAKE) in $(MAKEDIR) + * To regenerate, run "$(MAKE) clean" and "$(MAKE) all" on $(MAKEDIR) + */ +#ifndef __KHIMAIRA_VERSION_H +#define __KHIMAIRA_VERSION_H + +/* Version number macros */ +#define KH_VERSION_MAJOR $(KHIMAIRA_VERSION_MAJOR) +#define KH_VERSION_MINOR $(KHIMAIRA_VERSION_MINOR) +#define KH_VERSION_PATCH $(KHIMAIRA_VERSION_PATCH) +#define KH_VERSION_AUX $(KHIMAIRA_VERSION_AUX) +#define KH_VERSION_LIST $(KHIMAIRA_VERSIONC) +#define KH_VERSION_STRING "$(KHIMAIRA_VERSION)" +#define KH_VERSION_STRINGW L"$(KHIMAIRA_VERSION)" +#define KH_VERSION_STRINGC "$(KHIMAIRA_VERSIONC)" +#define KH_VERSION_STRINGCW L"$(KHIMAIRA_VERSIONC)" + +/* Version definition macros */ +#define KH_VER_FILEFLAGS $(kh_fileflags) +#define KH_VER_FILEOS $(kh_fileos) +#define KH_VER_FILETYPEDLL $(kh_filetype_dll) +#define KH_VER_FILETYPEAPP $(kh_filetype_app) + +/* Language specific version strings */ +#define KH_VERSTR_COMPANY_1033 "$(KHIMAIRA_SRC_COMPANY_1033)" +#define KH_VERSTR_COPYRIGHT_1033 "$(KHIMAIRA_SRC_COPYRIGHT_1033)" +#define KH_VERSTR_PRODUCT_1033 "$(KHIMAIRA_PRODUCT_1033)" +#define KH_VERSTR_VERSION_1033 "$(KHIMAIRA_VERSION_STR_1033)" + +!ifdef KHIMAIRA_COMMENT_STR_1033 +#define KH_VERSTR_COMMENT_1033 "$(KHIMAIRA_COMMENT_STR_1033)" +#define KH_VERSTR_BUILDINFO_1033 KH_VERSTR_COMMENT_1033 +!endif +!ifdef KHIMAIRA_PRIVATE_STR_1033 +#define KH_VERSTR_PRIVATE_1033 "$(KHIMAIRA_PRIVATE_STR_1033)" +#define KH_VERSTR_BUILDINFO_1033 KH_VERSTR_PRIVATE_1033 +!endif +!ifdef KHIMAIRA_SPECIAL_STR_1033 +#define KH_VERSTR_SPECIAL_1033 "$(KHIMAIRA_SPECIAL_STR_1033)" +#define KH_VERSTR_BUILDINFO_1033 KH_VERSTR_SPECIAL_1033 +!endif +#endif +<< + +clean:: +! if exist($(VERSIONINC)) + $(RM) $(VERSIONINC) +! endif + diff --git a/src/windows/identity/config/Makefile.w32 b/src/windows/identity/config/Makefile.w32 new file mode 100644 index 000000000..1b862a9ad --- /dev/null +++ b/src/windows/identity/config/Makefile.w32 @@ -0,0 +1,278 @@ +# +# Khimaira : Win32 configuration makefile +# This file will be included by all the makefiles +# in the build tree. +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +!ifndef KHIMAIRA_WIN32_CONFIG +KHIMAIRA_WIN32_CONFIG=1 + +# Environment Variables +# The following environment variables MUST be set: +# KH_ROOT : Root of the source tree. +# KH_BUILD: One of DEBUG or RETAIL +# +# The following environment variables are optional: +# KH_RUNTIME: One of STATIC or DLL, specifies whether the CRT libs +# are linked statically or through MSVCRT.DLL. +# KH_AUXCFLAGS: Optional flags for CL +# KH_RELEASE: Release type. One of OFFICIAL, PRERELEASE, PRIVATE or SPECIAL. +# OFFICIAL : An official release of Khimaira +# PREPRELEASE: A beta/release candidate release +# PRIVATE : Private build +# SPECIAL : Special build. Typically one with non-mainline patches. + +# Version info +KHIMAIRA_VERSION_MAJOR=0 +KHIMAIRA_VERSION_MINOR=1 +KHIMAIRA_VERSION_PATCH=1 +KHIMAIRA_VERSION_AUX=0 +KHIMAIRA_VERSION=$(KHIMAIRA_VERSION_MAJOR).$(KHIMAIRA_VERSION_MINOR).$(KHIMAIRA_VERSION_PATCH).$(KHIMAIRA_VERSION_AUX) +KHIMAIRA_VERSIONC=$(KHIMAIRA_VERSION_MAJOR),$(KHIMAIRA_VERSION_MINOR),$(KHIMAIRA_VERSION_PATCH),$(KHIMAIRA_VERSION_AUX) + +# Source information +KHIMAIRA_SRC_COMPANY_1033=Massachusetts Institute of Technology + +KHIMAIRA_SRC_COPYRIGHT_1033=(C) 2005 Massachusetts Institute of Technology + +# Choose the default build type if one is not set +!if ("$(KH_BUILD)" != "DEBUG") && ("$(KH_BUILD)" != "RETAIL") +! if defined(NODEBUG) && "$(NODEBUG)"=="1" +KH_BUILD=RETAIL +! else +KH_BUILD=DEBUG +! endif +!endif + +!if "$(KH_BUILD)"=="DEBUG" && defined(NODEBUG) && "$(NODEBUG)"=="1" +! error The Khimaira build configuration is set for DEBUG while the Platform SDK build environment is set to RETAIL. +!endif + +# The default release type is PRIVATE is no other type is specified +!if ("$(KH_RELEASE)" != "OFFICIAL") && ("$(KH_RELEASE)" != "PRERELEASE") && ("$(KH_RELEASE)" != "PRIVATE") && ("$(KH_RELEASE)" != "SPECIAL") +KH_RELEASE=PRERELEASE +!endif + +# Version and build strings + +!if "$(KH_RELEASE)" == "OFFICIAL" +KHIMAIRA_VERSION_STR_1033=$(KHIMAIRA_VERSION) +KHIMAIRA_COMMENT_STR_1033=Official build. +!elseif "$(KH_RELEASE)" == "PRERELEASE" +KHIMAIRA_VERSION_STR_1033=$(KHIMAIRA_VERSION) Alpha +KHIMAIRA_COMMENT_STR_1033=Prerelease build. +!elseif "$(KH_RELEASE)" == "PRIVATE" +KHIMAIRA_VERSION_STR_1033=$(KHIMAIRA_VERSION).PRIVATE +KHIMAIRA_PRIVATE_STR_1033=Private build. +!elseif "$(KH_RELEASE)" == "SPECIAL" +KHIMAIRA_VERSION_STR_1033=$(KHIMAIRA_VERSION).SPECIAL +KHIMAIRA_SPECIAL_STR_1033=Special build. +!endif + +!if "$(KH_BUILD)" == "DEBUG" +KHIMAIRA_VERSION_STR_1033=$(KHIMAIRA_VERSION_STR_1033).DEBUG +!else +!endif + +KHIMAIRA_PRODUCT_1033=NetIDMgr $(KHIMAIRA_VERSION_STR_1033) + +# See what compiler we are using +# TODO: Update this to support other compilers +!if defined(MSVCVer) && "$(MSVCVer)"=="8.0" +KH_CLVER=vc8 +!else +KH_CLVER=vc7 +!endif + +# Check for required env vars +!ifndef MODULE +! error MODULE must be specified +!endif +!ifndef KH_ROOT +KH_ROOT=$(PISMERE)\athena\auth\krb5\src\windows\identity +!endif + +!ifdef NODEBUG +OUTPRE_DBG=rel +!else +OUTPRE_DBG=dbg +!endif +OUTPRE1=obj +OUTPRE2=$(OUTPRE1)\$(CPU) +OUTPRE3=$(OUTPRE2)\$(OUTPRE_DBG) +OUTPRE=$(OUTPRE3)^\ + + + +# Output directory structure +DESTROOT=$(KH_ROOT)\dest +OBJROOT=$(KH_ROOT)\obj +SRC=$(KH_ROOT) + +DESTDIR=$(DESTROOT)\$(CPU)\$(OUTPRE_DBG) +OBJDIR=$(OBJROOT)\$(CPU)\$(OUTPRE_DBG) + +OBJ=$(OBJDIR)\$(MODULE) +INCDIR=$(DESTDIR)\include +#BINDIR=$(DESTDIR)\bin +BINDIR=$(KH_ROOT)\$(OUTPRE) +#LIBDIR=$(DESTDIR)\lib +LIBDIR=$(KH_ROOT)\$(OUTPRE) +DOCDIR=$(DESTDIR)\doc + +# Source directories +CONFDIR=$(SRC)\config + +# Setup environment for win32.mak + +!if "$(KH_BUILD)" == "RETAIL" +NODEBUG=1 +!endif + +# Win32.mak +!include + +# Program macros + +CD=cd +RM=del /q +MKDIR=mkdir +RMDIR=rmdir +ECHO=echo +MAKECMD=nmake +CP=copy /y +LINK=link +CCSV=perl $(SRC)\config\ccsv.pl +MC=mc + +!ifdef KH_DOXYFULLPATH +DOXYGEN=$(KH_DOXYFULLPATH) +!else +DOXYGEN=doxygen +!endif + +!ifdef KH_HHCFULLPATH +HHC=$(KH_HHCFULLPATH) +!else +HHC=hhc +!endif + +!ifdef KH_KFWPATH +KFWINCDIR=$(KH_KFWPATH)\inc +kfwincflags = -I$(KFWINCDIR)\krb5 -I$(KFWINCDIR)\krb5\KerberosIV -I$(KFWINCDIR)\loadfuncs -I$(KFWINCDIR) +KFWLIBDIR=$(KH_KFWPATH)\lib\$(CPU) +!else +KFWINCDIR=$(PISMERE)\athena\auth\krb5\src\include +kfwincflags = -I$(KFWINCDIR) -I$(PISMERE)\athena\util\loadfuncs -I$(PISMERE)\athena\auth\krb5\src\include\kerberosIV -I$(PISMERE)\athena\auth\krb4\include +KFWLIBDIR=$(PISMERE)\target\lib\$(CPU)\$(OUTPRE_DBG) +!endif + +!ifdef KH_AFSPATH +AFSINCDIR=$(KH_AFSPATH)\include +AFSLIBDIR=$(KH_AFSPATH)\lib +afsincflags=-I$(AFSINCDIR) +!endif + +#EXTLIBDIR=$(SRC)\ext-lib\$(CPU) +#EXTINCDIR=-I$(SRC)\ext-inc + +incflags= -I$(INCDIR) -I$(SRC)\include -I. -I$(OBJ) $(kfwincflags) $(afsincflags) +rincflags= /i $(INCDIR) /i $(SRC)\include /i . +khdefines=-DUNICODE -D_UNICODE +khcwarn=/Wp64 +!ifndef KH_NO_WX +khcwarn=$(khcwarn) /WX +!endif + +khcflags=$(cdebug) $(cflags) $(incflags) $(khdefines) $(khcwarn) +khlguiflags=$(ldebug) $(guilflags) +khlconflags=$(ldebug) $(conlflags) +khldllguiflags=$(ldebug) $(dlllflags) +khldllconflags=$(ldebug) $(dlllflags) + +!if "$(KH_RUNTIME)" == "STATIC" +khcflags=$(khcflags) $(cvarsmt) +khlguiflags=$(khlguiflags) $(guilibsmt) +khlconflags=$(khlconflags) $(conlibsmt) +khldllguiflags=$(khldllguiflags) $(guilibsmt) +khldllconflags=$(khldllconflags) $(conlibsmt) +!else +khcflags=$(khcflags) $(cvarsdll) +khlguiflags=$(khlguiflags) $(guilibsdll) +khlconflags=$(khlconflags) $(conlibsdll) +khldllguiflags=$(khldllguiflags) $(guilibsdll) +khldllconflags=$(khldllconflags) $(conlibsdll) +!endif + +C2OBJ=$(CC) $(khcflags) $(KH_AUXCFLAGS) /Fo"$@" /c $** + +EXECONLINK=$(LINK) /NOLOGO $(khlconflags) /OUT:$@ $** + +EXEGUILINK=$(LINK) /NOLOGO $(khlguiflags) /OUT:$@ $** + +DLLCONLINK=$(LINK) /NOLOGO $(khldllconflags) /OUT:$@ /IMPLIB:$(LIBDIR)\$(@B).lib $** + +DLLGUILINK=$(LINK) /NOLOGO $(khldllguiflags) /OUT:$@ /IMPLIB:$(LIBDIR)\$(@B).lib $** + +DLLRESLINK=$(LINK) /NOLOGO /DLL /NOENTRY /MACHINE:$(PROCESSOR_ARCHITECTURE) /OUT:$@ $** + +RC2RES=$(RC) $(RFLAGS) $(rincflags) /fo $@ $** + +MC2RC=$(MC) $(MCFLAGS) -h $(OBJ)\ -m 1024 -r $(OBJ)\ -x $(OBJ)\ $** + +{}.c{$(OBJ)}.obj: + $(C2OBJ) + +{$(OBJ)}.c{$(OBJ)}.obj: + $(C2OBJ) + +{}.h{$(INCDIR)}.h: + $(CP) $** $@ + +{}.rc{$(OBJ)}.res: + $(RC2RES) + +{$(OBJ)}.rc{$(OBJ)}.res: + $(RC2RES) + +clean:: +!if exist($(OBJ)) + $(RM) $(OBJ)\ +!endif + +test:: + +mkdirs:: +!if !exist($(OBJ)) + $(MKDIR) $(OBJ) +!endif + +TAGFILE = $(SRC)\TAGS + +etag:: + etags -o $(TAGFILE) -a *.c *.h + +.SUFFIXES: .h + +!endif diff --git a/src/windows/identity/config/ccsv.pl b/src/windows/identity/config/ccsv.pl new file mode 100644 index 000000000..c6c82814f --- /dev/null +++ b/src/windows/identity/config/ccsv.pl @@ -0,0 +1,124 @@ +#!/usr/bin/perl + +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, copy, +# modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + + +# This is a simple script that is used for generating C code from CSV +#files. We expect three arguments, the which is the .csv file +#to be parsed, a which is a configuration file and the +#. + +# The configuration file is a perl file which defines the following +#variables : + +# $skip_lines : the number of lines to skip in the csv. The default is 0 + +# @pquote : an array of boolean integers that specify whether or not +# to quote the specific field using double quotes. The default is to +# not quote anything. + +# $file_prefix : the prefix for the file + +# $record_prefix : the prefix for each record + +# $field_sep : the field separator. The default is ',' + +# $record_postfix : the postfix for each record + +# $record_sep : A record separator. Only shows up between records. + +# $file_postfix : the postfix for the entire file + +use Text::ParseWords; + +sub do_nothingus { +} + +if($#ARGV != 2) { + print "Usage: ccsv.pl \n"; + die; +} + +$infn=$ARGV[0]; +$cfgfn=$ARGV[1]; +$outfn=$ARGV[2]; + +$skip_lines = 0; +@pquote = {}; +$file_prefix = ""; +$record_prefix = ""; +$field_sep = ","; +$record_postfix = ""; +$record_sep = "\n"; +$file_postfix = ""; +$record_parser = \&do_nothingus; + +($inbase) = ($infn =~ m/^(\w*)/); + +do $cfgfn; + +open(IN, "<".$infn) or die "Can't open input file:".$infn; +open(OUT, ">".$outfn) or die "Can't open output file:".$outfn; + +print OUT $file_prefix; + +$first_line = 1; + +while() { + chomp $_; + if($skip_lines > 0) { + $skip_lines--; + } elsif (m/^\#/) { + # ignore + } else { + if($first_line == 0){ + print OUT $record_sep; + } else { + $first_line = 0; + } + + @fields = &parse_line(',',0,$_); + for(@fields) { + chomp; + s/^\s*//; + } + + &$record_parser(\@fields); + + print OUT $record_prefix; + for(my $i=0; $i <= $#fields; $i++) { + print OUT $field_sep if $i != 0; + print OUT 'L"' if $pquote[$i] == 1; + print OUT $fields[$i]; + print OUT '"' if $pquote[$i] == 1; + } + print OUT $record_postfix; + } +} + +print OUT $file_postfix; + +close INF; +close OUT; diff --git a/src/windows/identity/config/csvschema.cfg b/src/windows/identity/config/csvschema.cfg new file mode 100644 index 000000000..ba3bf9bfc --- /dev/null +++ b/src/windows/identity/config/csvschema.cfg @@ -0,0 +1,67 @@ +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, copy, +# modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + +$file_prefix = < + +kconf_schema schema_$inbase\[] = { +EOS + +$record_prefix = "{"; + +$record_sep = ",\n"; + +$record_postfix = "}"; + +$file_postfix = < + +CONFFILE=$(OBJ)\DoxyConf.cfg + +all: mkdirs docs + +docs: + $(DOXYGEN) << +@INCLUDE = doxyfile.cfg + +PROJECT_NUMBER = "$(KHIMAIRA_VERSION)" + +OUTPUT_DIRECTORY = "$(DOCDIR)" + +STRIP_FROM_PATH = "$(SRC)" + +INTERNAL_DOCS = NO + +WARN_LOGFILE = "$(OBJ)\doxywarnings.txt" + +INPUT = "$(SRC)\include" +INPUT += "$(SRC)\kconfig" +INPUT += "$(SRC)\kcreddb" +INPUT += "$(SRC)\khlog" +INPUT += "$(SRC)\kmq" +INPUT += "$(SRC)\ui" +INPUT += "$(SRC)\uilib" +INPUT += "$(SRC)\util" +INPUT += "$(SRC)\doc" +INPUT += "$(SRC)\kmm" +INPUT += "$(SRC)\kherr" + +IMAGE_PATH = "$(SRC)\doc\images" + +INCLUDE_PATH = "$(INCDIR)" "$(SRC)\include" + +CHM_FILE = "$(DOCDIR)\devdocs.chm" +<< + -$(HHC) $(DOCDIR)\html\index.hhp + +clean:: + $(RMDIR) /s $(DOCDIR)\html + $(RM) $(DOCDIR)\*.* diff --git a/src/windows/identity/doc/cred_aquisition.h b/src/windows/identity/doc/cred_aquisition.h new file mode 100644 index 000000000..1adb3b8f5 --- /dev/null +++ b/src/windows/identity/doc/cred_aquisition.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/*! \page cred_acq Managed credential acquisition + + Credential providers and the identity provider must participate in + managed credential acquisition in order to respond to the user's + requests to obtain new credentials for an identity or to obtain + new credentials for an existing identity. + + There are two major processes that result in managed credential + acuqisition. One is the acquisition of initial credentials, while + the other is the acquisition of new crednetials. Both processes + acquire new credentials (or replace existing credentials with new + ones). The difference between the two processes lie in the way the + new credentials are obtained. Initial credentials are obtained + using user supplied username and password while new credentials + are obtained using other existing credentials. + + \section cred_acq_init Initial Credentials + + When a user initiates the process of initial credential + acquisition, NetIDMgr broadcasts a + <::KMSG_CRED,::KMSG_CRED_INITIAL_CREDS> message. Credential + providers which need to participate in the initial credential + acquisition should respond to this message as detailed in + \ref cred_acq_handle. + + \section cred_acq_new New Credentials + + When a user initiates the process of obtaining new credentials + based on existing credentials, NetIDMgr broadcasts a + <::KMSG_CRED,::KMSG_CRED_NEW_CREDS> message. Credential providers + which need to participate in the initial credential acquisition + should respond to this message as detailed in \ref cred_acq_handle. + + The following pages provide detailed information: + + - \subpage cred_acq_new_resp + - \subpage cred_acq_dlgproc + */ + +/*! \page cred_acq_new_resp Handling new credentials acquisition + + The process of acquiring new credentials whether they are initial + credentials or not, happen as follows : + + - NetIDMgr creates a ::khui_new_creds object and a credentials + acquisition window. + + - <::KMSG_CRED,::KMSG_CRED_INITIAL_CREDS> or + <::KMSG_CRED,::KMSG_CRED_NEW_CREDS> is sent to all the + credentials providers. + + - The credential providers create the panels (where appropriate) + for customizing their respective credential types. The type, + panel and any dependency information is populated into a + ::khui_new_creds_by_type structure and added to the + ::khui_new_creds structure. + + - <::KMSG_CRED, ::KMSG_CRED_DIALOG_PRESTART> is sent to all the + credentials providers. Credentials providers should use this + message to finialize initialization in preparation of showing + the credentials acquisition window, such as by initializing the + controls of the individual panels. + + - <::KMSG_CRED, ::KMSG_CRED_DIALOG_START> is sent to all the + credentials providers. + + - The dialog for obtaining credentials is displayed. + Notifications between the main dialog and the individual panels + are done through ::KHUI_WM_NC_NOTIFY messages to the dialog + procedures. + + - Once the dialog completes, NetIDMgr sends + <::KMSG_CRED,::KMSG_CRED_DIALOG_END> message to all the + credentials providers. The UI portion ends here. The + individual dialog controls are destroyed as a result of the main + credentials acquisition window being destroyed. + + - NetIDMgr posts <::KMSG_CRED,::KMSG_CRED_DIALOG_PROCESS> message + to all the credentials providers. Each provider should check if + the user cancelled the dialog or indicated that the new + credentials should be obtained and act accordingly. The + credentials provider is responsible for removing the + ::khui_new_creds_by_type structre from the ::khui_new_creds + structure and freeing up any resources it allocated earlier in + preparation for obtaining new credentials. + + \section cred_acq_handle Responding to credential acquisition messages + + The credential acquisition messages are + <::KMSG_CRED,::KMSG_CRED_INITIAL_CREDS> and <::KMSG_CRED, + ::KMSG_CRED_NEW_CREDS>. They are structured as follows : + + - \b type : ::KMSG_CRED + - \b subtype: ::KMSG_CRED_INITIAL_CREDS or ::KMSG_CRED_NEW_CREDS + - \b uparam : 0 (unused) + - \b vparam : a pointer to a ::khui_new_creds structure. + + The \a vparam parameter of the message, as shown above, is a + pointer to a ::khui_new_creds structure. You can use the \a + subtype field of this structure to determine whether this is an + initial credentials acquisition or a new credentials acquisition + at any point. + + In response to this message, a credentials provider is expected to + provide a configuration panel which the user can use to customize + how the credentials of this type are to be obtained. The panel is + described by the ::khui_new_cred_panel structure. + + \subsection cred_acq_panel_spec Specifying the credentials type panel + + The credentials type panel is used by the user to customize how + credentials of the specified type are to be obtained. The + ::khui_new_cred_panel structure that describes the panel can be + used to specify a number of parameters that guide how the panel is + to be displayed in the new credentials acquisition dialog. + + The \a name field defines a localized string that will be + displayed in the tab control that houses the panel. Optionally, + an icon can be specified in the \a icon field which will appear + alongside the name. A tooltip may be provided in the \a tooltip + field which will be displayed when the user hovers the mouse over + the tab. + + In order to assert that the tab appears at a specific position in + the list of tabs, you can specify a positive number in the \a + ordinal field. Zero does not count as a valid ordinal. The + panels with positive ordinals are arranged first in increasing + order of ordinal (conflicts are resolved by sorting along the \a + name). Then the panels without a positive ordianl are arranged + behind these in increasing order of \a name. + + The \a hwnd_panel field is used to specify the handle to the + dialog or window of the panel. The parent of this window should + be set to the \a hwnd parameter of the ::khui_new_creds structure + which is passed in to the message handler. + + Following is a code snippet which suggests how this could be done: + + \code + // Message handling code for KMSG_CRED_NEW_CREDS or + // KMSG_CRED_INIT_CREDS + ... + khui_new_creds * c; + khui_new_creds_by_type * t; + + c = (khui_new_creds *) vparam; + t = malloc(sizeof(*t)); + ZeroMemory(t, sizeof(*t)); + + t->type = my_cred_type; + + // set look and feel params + t->ordinal = 3; // third in line + t->name = L"My panel name"; + t->icon = LoadIcon(my_hInstance, MAKEINTRESOURCE(IDI_PANEL_ICON)); + t->tooltip = L"Configure credentials of my type"; + + t->hwnd_panel = CreateDialog( + my_hInstance, + MAKEINTRESOURCE(IDD_MY_PANEL), + c->hwnd, + my_dialog_proc); + + if(KHM_FAILED(khui_cw_add_type(c,t))) { + // handle error + } + \endcode + + It is important to note that the ::khui_new_creds_by_type pointer + that is passed into khui_cw_add_type() points to an allocated + block of memory which should remain in memory until + <::KMSG_CRED,::KMSG_CRED_DIALOG_PROCESS> message is received. + + For information on how the dialog procedure should be written, see + \ref cred_acq_dlgproc . + +*/ + +/*! \page cred_acq_dlgproc Writing the dialog procedure for a cred type panel + + +*/ diff --git a/src/windows/identity/doc/cred_data_types.h b/src/windows/identity/doc/cred_data_types.h new file mode 100644 index 000000000..3257520e1 --- /dev/null +++ b/src/windows/identity/doc/cred_data_types.h @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/*! \page cred_data_types Data types in NetIDMgr + + NetIDMgr's Credentials Database supports several useful data types. In + addition, plug-ins can define custom data types. Only a few operations + are expected of these data types since the core KCDB delegates fine grained + operations to other entities that understand the underlying format. + + A field in a credential can have any one of these data types, but it must + have some data type. Each value can be at most \a KCDB_TYPE_MAXCB bytes + in length regardless of the data type. + + Some data types have a fixed size (such as \a Int32), while others are + variable size. The required memory for each field in a credential is + allocated as needed. + + \section kcdb_pg_dt Data types + + Descriptions of individual data types are below. + + \subsection kcdb_pg_idt Individual data types + + \subsubsection kcdb_pg_idt_v Void + + Pretty useless. This data type is used to indicate that the associated + object doesn't actually contain any data. + + \subsubsection kcdb_pg_idt_s String + + A unicode string that is terminated with a unicode NULL (L'\\0'). By + default, the type has the following flags : + + \a KCDB_TYPE_FLAG_CB_AUTO + + This is because, as long as the string is terminated with a unicode NULL, + the length of the string, and therefore it's size in bytes, can be inferred + from the data itself. + + \subsubsection kcdb_pg_idt_d Date + + Dates and times in NetIDMgr are stored as \a FILETIME structures. Utility + functions are provided for converting from other formats such as \a time_t. + + \subsubsection kcdb_pg_idt_i Interval + + Stores an interval of time. Stored as a 64 bit signed integer. The + string representation of this data type is different from the \a + Date data type and designate an interval of time. + + The special value _I64_MAX (which is defined in limits.h as + 0x7fffffffffffffff, or in otherwords, the largest positive value + that can be stored in a 64 bit signed integer) is used to + represent an interval of unknown length. + + The string representations of a data value of Interval type are + defined as follows for English (US): + + - "(Unknown)" if the value is _I64_MAX + + - "(Expired)" if the value is less than zero + + - "%d days %d hours" if the value is greater than 24 hours + + - "%d hours %d mins" if the value is greater than 1 hour + + - "%d mins %d secs" if the value is greater than 1 minute + + - "%d seconds" otherwise + + \subsubsection kcdb_pg_idt_i32 Int32 + + A signed 32 bit integer. + + \subsubsection kcdb_pg_idt_i64 Int64 + + A signed 64 bit integer. + + \subsubsection kcdb_pg_idt_da Data + + Raw data. Can contain a byte stream. This data type can be used by + plug-ins to associate raw data with a credential. However, there is no + built in string representation for this data type. As such, this is not + meant to be used for storing anything that has to be displayed to the user + verbatim. + + \section kcdb_pg_cust Custom data types + + \subsection kcdb_pg_cb Custom data type call backs + + Custom data types in the NetIDMgr Credentials Database are defined using + \a kcdb_type structures which must include several callback functions. + The expected behavior of these callback functions is documented below. + + \subsubsection kcdb_pg_cb_ts toString + + \code + khm_int32 toString( + const void * data, + khm_int32 cb_data, + wchar_t *buffer, + khm_int32 *pcb_buffer, + khm_int32 flags); + \endcode + + Produce the localized string representation of the object pointed to by + \a data. The size of the data block is specified by the \a cb_data + parameter. If the data type specified the \a KCDB_TYPE_FLAG_CB_AUTO flag + then \a cb_data can be \a KCDB_CBSIZE_AUTO, in which case the size of the + data block is to be inferred. + + \a toString should assume that the block of data pointed to by \a data is + valid for this data type. + + The \a pcb_buffer parameter is always a valid pointer to an \a khm_int32 + variable. + + The \a buffer parameter is a pointer to a \a wchar_t buffer which is to + receive the unicode string representing the object. \a buffer may be + \a NULL, in which case the required size of the buffer should be returned + in \a pcb_buffer. In this case, the function should return + \a KHM_ERROR_TOO_LONG. + + If the \a buffer parameter is not \a NULL and the \a pcb_buffer specifies + that the buffer is large enough to hold the string representation, the + function should copy the string representation to the buffer, set the + \a pcb_buffer to the number of bytes that were copied including the + terminating \a NULL, and return \a KHM_ERROR_SUCCESS. + + If the \a buffer parameter is not \a NULL and the \a pcb_buffer specifies + a buffer that is not large enough, the function should set \a pcb_buffer + to the required size (including the terminating \a NULL) and then return + \a KHM_ERROR_TOO_LONG. + + \subsubsection kcdb_pg_cb_cmp comp + + \code + khm_int32 comp( + const void * data1, + khm_int32 cb_data1, + const void * data2, + khm_int32 cb_d2); + \endcode + + Compares two objects and returns a value indicating the relative ordering. + + Since the KCDB does not interpret any data type, it relies on a loose + definition of what a relative ordering is. It is left up to each data + type callback to interpret what 'ascending' and 'descending' mean. + + The return value \a r should be as follows : + + \a r < 0 : if \a data1 < \a data2 + + \a r > 0 : if \a data1 > \a data2 + + \a r = 0 : if \a data1 = \a data2 or no relative ordering can be determined + for the two objects \a data1 and \a data2. + + The function should assume that both objects are valid for this data type. + + The size specifiers \a cb_data1 and \a cb_data2 can (either or both) be + \a KCDB_CBSIZE_AUTO if the data type specified \a KCDB_TYPE_FLAG_CB_AUTO + flag. + + \subsubsection kcdb_pg_cb_dup dup + + \code + khm_int32 dup( + const void * d_src, + khm_int32 cb_src, + void * d_dst, + khm_int32 * pcb_dst); + \endcode + + Duplicate an object. The object pointed to by \a d_src is to be copied to + the buffer pointed to by \a d_dst. The function is to assume that \a d_src + object is valid. The size specifier \a cb_src may be \a KCDB_CBSIZE_AUTO + if \a KCDB_TYPE_FLAG_CB_AUTO was specified for the data type. + + If \a d_dst pointer is \a NULL, then the required buffer size should be + returned in \a pcb_dst. In this case, the function itself should return + \a KHM_ERROR_TOO_LONG. The same behavior should occur if \a d_dst is non + \a NULL and \a pcb_dst indicates that the buffer is not sufficient. + + If \a d_dst is not \a NULL and \a pcb_dst indicates that the buffer is + sufficient, then a copy of the object in \a d_src should be placed in + \a d_dst. The function shold return \a KHM_ERROR_SUCCESS and set + \a pcb_dst to the number of bytes that were copied. + + This callback will only be called when the credentials database is + retrieving objects from the outside. Once it receives an object it may be + copied or moved as required. Hence the object should not assume to reside + in a specific location of memory. Also, \a dup is not intended to perform + such functions as reference counting which require knowledge of a precise + number of instances of an object, as the credentials database may copy + the object simply by copying the block of memory. + + Note that whenever \a pcb_dst is to be set, it MUST be set to a valid byte + count. It can not be assigned \a KCDB_CBSIZE_AUTO even if the data type + supports it. The \a pcb_dst parameter is used internally to allocate + memory for the object. + + \subsubsection kcdb_pg_cb_iv isValid + + \code + khm_boolean isValid( + const void * data, + khm_int32 cb_data); + \endcode + + Checks if the object pointed to by the \a data pointer is a valid object + for this data type. If the data type specified the \a KCDB_TYPE_CB_AUTO + flag, then the \a cb_data parameter may be \a KCDB_CBSIZE_AUTO, in which + the size of the object should be inferred from the data. + + The function should be able to determine the validity of the object and + return \a TRUE if it is valid. Return \a FALSE if it isn't, or if the + size of the object can not be inferred from the given data, or if the + inferred size exceeds \a KCDB_TYPE_MAXCB. + +*/ diff --git a/src/windows/identity/doc/cred_main.h b/src/windows/identity/doc/cred_main.h new file mode 100644 index 000000000..e8f7d2999 --- /dev/null +++ b/src/windows/identity/doc/cred_main.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/*! \page cred Credentials Providers + + \section cred_contents Contents + + - \subpage cred_data_types + - \subpage cred_acq + - \subpage cred_prop_pages + - \subpage cred_msgs +*/ diff --git a/src/windows/identity/doc/cred_msgs.h b/src/windows/identity/doc/cred_msgs.h new file mode 100644 index 000000000..a1b2c2cc2 --- /dev/null +++ b/src/windows/identity/doc/cred_msgs.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/*! \page cred_msgs Handling credentials provider messages + +A credentials provider plugin receives a number of messages during the +course of execution. This section describes the appropriate ways of +handling these messages. + +\section pi_credmsg_system System mesages + +There are only two system messages that a credentials provider needs +to handle. Both of these are explained elsewhere as they deal with +initialization and uninitialization of the plugin. See the following +two sections for details on handling these messages. + +- <::KMSG_SYSTEM,::KMSG_SYSTEM_INIT> \ref pi_pt_cred_init +- <::KMSG_SYSTEM,::KMSG_SYSTEM_EXIT> \ref pi_pt_cred_exit + +\section pi_credmsg_cred Credential messages + + + +*/ diff --git a/src/windows/identity/doc/cred_prop_pages.h b/src/windows/identity/doc/cred_prop_pages.h new file mode 100644 index 000000000..5e844833f --- /dev/null +++ b/src/windows/identity/doc/cred_prop_pages.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/*! \page cred_prop_pages Property Pages for Credentials + + This section describes the logistics of property pages. When a + user selects the 'Properties' option from a menu (either the File + menu or a context menu), then a KHUI_ACTION_PROPERTIES action is + triggered. This is handled by the credentials window and triggers + the launch of a property sheet if there is a valid context to + extract properties from. + + Sequence of actions: + + - KHUI_ACTION_PROPERTIES action is triggered. + + - The main window dispatches the action to the credentials window. + + - If there is a valid context, then the credentials window calls + khui_ps_create_sheet() to create an empty property sheet + structure of type ::khui_property_sheet. The \a ctx member of + the structure is populated with the property context obtained + through khui_context_get(). + + - A global message is broadcast of type + <::KMSG_CRED,::KMSG_CRED_PP_BEGIN> with the parameter blob that + is a pointer to the ::khui_property_sheet structure. + + - Subscribers to <::KMSG_CRED> messages handle the message, check + the \a ctx member of the structure and determine whether or not + and what type property pages to add to the property sheet. New + property sheets are added by calling khui_ps_add_page(). + + - Once all the pages are added, a + <::KMSG_CRED,::KMSG_CRED_PP_PRECREATE> message is broadcast. + This is a chance for the property page providers to do any + processing before the property page is created. + + - The property sheet is created and made visible with a call to + khui_ps_show_sheet(). + + - The NetIDMgr message loop takes over. Further interaction + including notifications of 'Ok','Cancel','Apply' and other + property sheet related actions are handled through WIN32 + messages. + + - Once the user closes the property sheet, a + <::KMSG_CRED,::KMSG_CRED_PP_END> message is sent to all + subscribers. Individual subscribers who added pages to the + property sheet must free up any associated resources at this + point. + + - All the ::khui_property_page structures that were allocated as + well as the ::khui_property_sheet structure are freed up with a + call to khui_ps_destroy_sheet(). + +The maximum number of property sheets that can be open at one time is +currently set to 256. Each property sheet can have a maximum of 16 +property pages. + */ diff --git a/src/windows/identity/doc/doxyfile.cfg b/src/windows/identity/doc/doxyfile.cfg new file mode 100644 index 000000000..7aac8021b --- /dev/null +++ b/src/windows/identity/doc/doxyfile.cfg @@ -0,0 +1,1000 @@ +# Doxyfile 1.2.18 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = NetIDMgr + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, +# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en +# (Japanese with english messages), Korean, Norwegian, Polish, Portuguese, +# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these class will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will +# prepend the brief description of a member or function before the +# detailed description. Note: if both HIDE_UNDOC_MEMBERS and +# BRIEF_MEMBER_DESC are set to NO, the brief descriptions will be +# completely suppressed. + +REPEAT_BRIEF = YES + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show +# all inherited members of a class in the documentation of that class +# as if those members were ordinary class members. Constructors, +# destructors and assignment operators of the base classes will not be +# shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. It is allowed to use relative paths in the argument list. + +STRIP_FROM_PATH = + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = YES + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower case letters. If set to YES upper case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# users are adviced to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explict @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = YES + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# reimplements. + +INHERIT_DOCS = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consist of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. +# For instance some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +SHOW_DIRECTORIES = NO + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp +# *.h++ *.idl *.odl + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories +# that are symbolic links (a Unix filesystem feature) are excluded from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = header.html + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = footer.html + +# The HTML_STYLESHEET tag can be used to specify a user defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet + +HTML_STYLESHEET = stylesheet.css + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = YES + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output dir. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non empty doxygen will try to run +# the html help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the Html help documentation and to the tree view. + +TOC_EXPAND = YES + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one +# that is generated for HTML Help). For this to work a browser that +# supports JavaScript and frames is required (for instance Mozilla, +# Netscape 4.0+, or Internet explorer 4.0+). Note that for large +# projects the tree generation can take a very long time. In such +# cases it is better to disable this feature. Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimised for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assigments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_XML = NO + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. + +PREDEFINED = _WIN32 \ + UNICODE \ + _UNICODE + +# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are +# alone on a line, have an all uppercase name, and do not end with a +# semicolon. Such function macros are typically used for boiler-plate +# code, and will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tagfiles. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. Note that this +# option is superceded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yield more powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermedate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO + +# The CGI_NAME tag should be the name of the CGI script that +# starts the search engine (doxysearch) with the correct parameters. +# A script with this name will be generated by doxygen. + +CGI_NAME = search.cgi + +# The CGI_URL tag should be the absolute URL to the directory where the +# cgi binaries are located. See the documentation of your http daemon for +# details. + +CGI_URL = + +# The DOC_URL tag should be the absolute URL to the directory where the +# documentation is located. If left blank the absolute path to the +# documentation, with file:// prepended to it, will be used. + +DOC_URL = + +# The DOC_ABSPATH tag should be the absolute path to the directory where the +# documentation is located. If left blank the directory on the local machine +# will be used. + +DOC_ABSPATH = + +# The BIN_ABSPATH tag must point to the directory where the doxysearch binary +# is installed. + +BIN_ABSPATH = /usr/local/bin/ + +# The EXT_DOC_PATHS tag can be used to specify one or more paths to +# documentation generated for other projects. This allows doxysearch to search +# the documentation for these projects as well. + +EXT_DOC_PATHS = diff --git a/src/windows/identity/doc/footer.html b/src/windows/identity/doc/footer.html new file mode 100644 index 000000000..13314c2b0 --- /dev/null +++ b/src/windows/identity/doc/footer.html @@ -0,0 +1,19 @@ +
+ + + + + + +
+
+ Generated on $datetime for $projectname $projectnumber by Doxygen $doxygenversion
+ © 2004 Massachusetts Institute of Technology. Contact khimaira@mit.edu
+
+
+
+ +
+ + + diff --git a/src/windows/identity/doc/header.html b/src/windows/identity/doc/header.html new file mode 100644 index 000000000..4235468f3 --- /dev/null +++ b/src/windows/identity/doc/header.html @@ -0,0 +1,5 @@ + + +$title + + diff --git a/src/windows/identity/doc/images/Thumbs.db b/src/windows/identity/doc/images/Thumbs.db new file mode 100644 index 0000000000000000000000000000000000000000..371f5d62e6fde1193c679492a2d4c88b39a82638 GIT binary patch literal 28160 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 Sc)xCkB3GW)%m2MK@P7cv76Cc{ literal 0 HcmV?d00001 diff --git a/src/windows/identity/doc/images/credview-select-outline.jpg b/src/windows/identity/doc/images/credview-select-outline.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d06ca9f88cc2b5f816b837f4a3d2247186df5c0b GIT binary patch literal 16084 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 Xg7(?pxrzMKb^#C2hN!Pd`>6i`gPRKu literal 0 HcmV?d00001 diff --git a/src/windows/identity/doc/images/khimaira_logo_old.jpg b/src/windows/identity/doc/images/khimaira_logo_old.jpg new file mode 100644 index 0000000000000000000000000000000000000000..10e8fde4dbe415603d7b48348d27fc09eb92b4c8 GIT binary patch literal 9550 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 literal 0 HcmV?d00001 diff --git a/src/windows/identity/doc/images/khimaira_logo_small.png b/src/windows/identity/doc/images/khimaira_logo_small.png new file mode 100644 index 0000000000000000000000000000000000000000..26c338007d5dc8534986c5dc5461d786c157a3d5 GIT binary patch literal 3970 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 Xg7(?pxrzMKb^#C2hN!Pd`>6i`gPRKu literal 0 HcmV?d00001 diff --git a/src/windows/identity/doc/images/khimaira_logo_small_old.jpg b/src/windows/identity/doc/images/khimaira_logo_small_old.jpg new file mode 100644 index 0000000000000000000000000000000000000000..94d8d1911394d8ad15cea9d6f6fe2920f2fb6305 GIT binary patch literal 1665 zcmbW!c{tR090%~?1PX&fU@#~Q4u>I-B1j}ZKT=FcSWrY#OiD^pOhN)Jr;I_%U}YsF_G|3N zs;J`dIBATgwx*i4vKmfp?+6GEha(Y4Q6y4S4K0CI`_IK~0#FD*6L18B6ahXI2#f-8 zTLBCJfS|mzdx8Hd5FePA7mnaZ3h)-{L;yYz7|aI&L!l4|Z##xJ2OucuJ_Q{TOw_>( zt{5Ulh|MfSU@fYf#T^G$lytpA*s$iATaE5_?3vrsOb2F#Oq1PH&Upytn8fJo44rqii%50%kDGEAJx>> z)jxjH@U-Ps>+81mj?S*Zp||gbM@Gk(Q`0jn_UzpJ!s6=Mmvzq9jc=P;y-0i=YI z-jwp{9_+-!3K~2nO*!4~zoLfUVMaQn6}^xmF-$LaWN{dhhwu4e3UOPX z(Wt#fZFsk2CZ2GLBA@Fc`$io#+rzrpepik{y0+>!v#oNZX3%V?dhK(r4i}(=*5}&F z)4$SOTIT^r){7SeO`lQ&Otpu#j4k~cOB<4g zqAiO9OgF>NG*zLHwo47|4CD1G|HrFbAc^%TeQH_IjSH}8g<3D|3Glfyrv2Vu7E%3y zY$K0`h1B#Vg^F`CuZeTo%)GRh%N*SAj9;m6s=&CJol5%8`46YZrzi-3mP> z9he1roEa`4U~E}3o|??co{G{oi$s3H)(D@8Uhh;DBs@eI#|e?X2Tc^FI^QkuXjis5 zP1W2iP3ik`zk>MMr>rupo%~>d45<GDl*3u{AfN7pH3%Npy1(}?Ua=2ZIF3>Chu zoXxY>P99p8DmJ23!A+u_xqvVeKWVL`yxZf3j}Y7kBhH!Gsk8iTP#L!ta0Y$%7>f`u zkg8mp(Ssy5`$;>aT(X7Ow6pco^VE>tm?b)Y{dx1;K&1~M-xt>n5g&q$ckfl-^x1JM z!p%Fe^ih%oh&`#_X4rnay~rTV;VNeYzsB+1ZrVn#3xf_TmnD2s%yBJ=>9g$`^V>|< z{dS7JnE~-k3IjrMh-i`jXw7i8R{l2sWeT;;q}GiG_nvStp|kAaODo zf$9@)W=y2ggLGS?J%{yVc8KZF9!-fDepyUk9gZs6F;hTZ&WfTPYd(0eKxWeAbO1f8 z9-a6&Tj_l3E6OvA6$IxuvH|^>CFZkcs}ItBtNO&`N>yoiKIYiFm2eZ&_w2WxdZ|=+ zY4q^VRf+2|(qQtruMSVwFKXyWZOt|wcp}i%nV(rvIK5?-#CaZw^G6xgo4fRI0lWyY zqo2r;Djy7RG(sgNmlCWOzKy)JYc9ff37)+$HZGq{(aI?OgqhdA3T9SUS{V8|iybe+VCgN#EcY2?`ql1HjaW*JoVV~D3vygqGOSeWeY+AWK(Uv3sE zGAj+ri3$%2GoUy}-U;`LkT1G_B)C4McmIUWMorwc62CLuhD@dFSOs4rC%e1b-p^%7 zE8&&dA<$)}AN?{rEYbcbwrs}D+sG?))e^0HJLfR48$OiOomWfK%@0msy7wdInformation Services and + Technology at Massachusetts + Institute of Technology +*/ + +/*! \page bugs Reporting bugs + + NetIDMgr bugs can be reported to + khimaira@mit.edu for now. + + In the future, there will actually be a place to track NetIDMgr bugs. + + When reporting bugs, please include as much information as + possible to help diagnose the problem. More guidelines about + reporting bugs will appear here at some point in time. + + \image html khimaira_logo_small.png +*/ + +/*! \page releases Prior releases + + - 0.1.1 (Charles Manson) [soon]\n + First alpha release. As stable as Charles Manson, hence the + name. + + - 0.1.2 (tbd) [tbd]\n + First beta release. +*/ diff --git a/src/windows/identity/doc/plugin_framework.h b/src/windows/identity/doc/plugin_framework.h new file mode 100644 index 000000000..dbf160080 --- /dev/null +++ b/src/windows/identity/doc/plugin_framework.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/*! +\page pi_framework Plugin Framework + +\section pi_fw_pnm Plugins and Modules + +\subsection pi_fw_pnm_p Plugins + +A NetIDMgr plugin is a package that implements a defined API that will +perform credentials management or related tasks on behalf of NetIDMgr. +The core NetIDMgr codebase does not interact directly with Kerberos of +AFS or any other external entity directly. Instead, plugins are used +to abstract out this task. + +Each plugin has a name. The name should be unique among the loaded +plugins, or the plugin will fail to load. + +The method in which NetIDMgr communicates with a plugin depends on the +plugin type. For more information on each plugin type, please refer +to \ref pi_pt. + +Most plugin types rely on a message processor for communication. +During plugin registration, the module specifies the message processor +for the plugin, which acts as the only point of contact between the +NetIDMgr core and the plugin. Some other plugins require exporting +specific functions. + +\subsection pi_fw_pnw_m Modules + +One or more plugins can be bundled together into a module. A module +is essentially a dynamically loadable library which contain a specific +set of callbacks. Currently, the only two required callbacks for a +module are : + +- init_module(), and +- exit_module() + +\section pi_fw_pm Plugin/Module Manager + +The plugin manager maintains a separate thread for loading and +registering modules. When a module is successfully loaded and it +registers one or more plugins, a new thread is created for each +plugin. Plugin specific initialization and other callback functions +are called from within this new thread. This is to prevent one plugin +from "hanging" other plugins and the main NetIDMgr UI threads. + +Read more : +- \ref pi_structure + +\subsection pi_fw_pm_load Load sequence + +When kmm_load_module() is called, the following sequence of events +happen. + +- The standard system search path is used to locate the binary. + +- The binary is loaded into the address space of NetIDMgr along with + any dependencies not already loaded. + +- If the NetIDMgr core binary is signed, then the signature is checked + against the system and user certificate stores. If this fails, the + module is unloaded. See \ref pi_fw_pm_unload. + +- init_module() for the loaded module is called. If this function + returns an error or if no plugins are registered, then the module is + unloaded. See \ref pi_fw_pm_unload. + +- During processing of init_module(), if any localized resource + libraries are specified using kmm_set_locale_info(), then one of the + localized libraries will be loaded. See \ref pi_localization + +- During processing of init_module(), the module registers all the + plugins that it is implementing by calling kmm_register_plugin() for + each. + +- Once init_module() returns, each plugin is initialized. The method + by which a plugin is initialized depends on the plugin type. The + initialization code for the plugin may indicate that it didn't + initialize properly, in which case the plugin is immediately + unregistered. No further calls are made to the plugin. + +- If no plugin is successfully loaded, the module is unloaded. See + \ref pi_fw_pm_unload. + +- During normal operation, any registered plugins for a module can be + unloaded explicitly, or the plugin itself may signal that it should + be unloaded. If at anytime, all the plugins for the module are + unloaded, then the module itself is also unloaded. + +\subsection pi_fw_pm_unload Unload sequence + +- For each of the plugins that are registered for a module, the exit + code is invoked. The method by which this happens depends on the + plugin type. The plugin is not given a chance to object to the + decision to unload. Each plugin is responsible for performing + cleanup tasks, freeing resources and unsubscribing from any message + classes that it has subscribed to. + +- exit_module() is called for the module. + +- If any localized resource libraries were loaded for the module, they + are unloaded. + +- The module is unloaded. + + */ diff --git a/src/windows/identity/doc/plugin_locale.h b/src/windows/identity/doc/plugin_locale.h new file mode 100644 index 000000000..3cb65a422 --- /dev/null +++ b/src/windows/identity/doc/plugin_locale.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/*! +\page pi_localization Localization + +If a module requires localized resources, it can register the +localized resource libraries with the module manager when it receives +the init_module() callback. Note that you can only register localized +resource libraries during init_module(). + +The localized resource library is global to a module. Each plugin is +not allowed to define its own localization library, although it is +free to load and use any library as it sees fit. The module manager +does not manage these libraries for the plugin. + +\section pi_loc_spec Specification of localized resources + +In order to register localized resource libraries, a module calls +kmm_set_locale_info(). The \a locales parameter to the function holds +a pointer to an array of ::kmm_module_locale records. Each record +specifies one language code and a filename of a library that holds the +language resources for that language. + +It is recommended that you use the LOCALE_DEF convenience macro when +defining locale records for use with kmm_set_locale_info(). This will +ensure that future changes in the API will only minimally affect your +code. For example: + +\code +kmm_module_locale my_locales[] = { +LOCALE_DEF(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US), L"english.dll", KMM_MLOC_FLAG_DEFAULT), +LOCALE_DEF(MAKELANGID(LANG_DUTCH,SUBLANG_DUTCH), L"dutch.dll", 0), +LOCALE_DEF(MAKELANGID(LANG_SPANISH,SUBLANG_SPANISH_MODERN), L"spanish.dll", 0) +}; + +int n_locales = sizeof(my_locales)/sizeof(my_locales[0]); + +... + +kmm_set_locale_info(h_module, my_locales, n_locales); + +... +\endcode + +See kmm_set_locale_info() and ::kmm_module_locale for more info. + +\section pi_loc_how Selection of localized resource library + +The module manager searches the array of ::kmm_module_locale objects +passed into the kmm_set_locale_info() function for one that matches +the current user locale (as opposed to the current system locale). A +record matches the locale if it has the same language ID. + +If a match is found, that library is selected. Otherwise, the list is +searched for one that is compatible with the current user locale. A +locale record is compatible with the user locale if the primary +language matches. + +If a match is still not found, the first record in the locale array +that has the ::KMM_MLOC_FLAG_DEFAULT flag set will be selected. + +If a match is still not found, then the kmm_set_locale_info() will +return ::KHM_ERROR_NOT_FOUND. + +\section pi_loc_usage Using localization + +The following convenience macros are available for using a module +handle to load resources from the corresponding resource library. +However, for performance reasons, it is advisable to obtain a handle +to the resource library loaded by the module manager using +kmm_get_resource_module() and then use it to access resources using +the regular WIN32 API. + +- ::kmm_LoadAccelerators +- ::kmm_LoadBitmap +- ::kmm_LoadCursor +- ::kmm_LoadIcon +- ::kmm_LoadImage +- ::kmm_LoadMenu +- ::kmm_LoadString + +*/ + diff --git a/src/windows/identity/doc/plugin_main.h b/src/windows/identity/doc/plugin_main.h new file mode 100644 index 000000000..ed8d038e2 --- /dev/null +++ b/src/windows/identity/doc/plugin_main.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/*! + +\page plugins NetIDMgr Modules and Plugins + +Plugins and localization are handled by the NetIDMgr Module Manager +API. Each plugin consists of a dynamically loadable library and zero +or more associated resource libraries. + +For more information about NetIDMgr Plugins, see the following +sections: + +- \subpage pi_framework +- \subpage pi_pt +- \subpage pi_structure +- \subpage pi_localization +*/ + +/*! \page pi_pt Plugin Types + +The types of plugins that are currently supported by NetIDMgr are : + +\section pi_pt_cred Credential Provider + +A credential provider plugin essentially acts as an interface between +NetIDMgr and some entity which defines the credentials for the purpose +of managing those credentials. + +There can be more than one credential provider in a module. + +\subsection pi_pt_cred_comm Communication + +Communication between NetIDMgr and a credential provider occurs +through a message processor. When registering a credential provider, +the module initialization code in init_module() specifies +::KHM_PITYPE_CRED as the \a type member and sets \a msg_proc member to +a valid message processor in the ::khm_plugin record. + +\subsection pi_pt_cred_init Initialization + +Once init_module() has completed, the module manager sends a +<::KMSG_SYSTEM,::KMSG_SYSTEM_INIT> message to the message processor. + +For credential provider plugins, <::KMSG_SYSTEM,::KMSG_SYSTEM_INIT> is +guaranteed to be the first message it receives. + +The callback function should return KHM_ERROR_SUCCESS if it +initializes properly or some other value otherwise. If the return +value signals an error, then the plugin is assume to not be loaded and +immediately unregistered. + +The message processor is automatically subscribed to the following +message types: +- ::KMSG_SYSTEM +- ::KMSG_KCDB + +Although a plugin can use the <::KMSG_SYSTEM,::KMSG_SYSTEM_INIT> +message enumerate existing credentials in the system, it should not +obtain new credentials. This is because other plugins that may depend +on the new credential messages may not be loaded at this time. See the +section on \ref cred_msgs for more information. + + +\subsection pi_pt_cred_exit Uninitialization + +When the plugin is to be removed, the module manager sends a +<::KMSG_SYSTEM,::KMSG_SYSTEM_EXIT> to the message processor. The +plugin must perform any necessary shutdown operations, free up +resources and unsubscribe from any messages that it has subscribed to. + +This message is guaranteed to be the last message received by a +credentials manager plugin if the plugin unsubsribes from all +additional message classes that it subsribed to. + +The message types that the message processor is automatically +subscribed to (See \ref pi_pt_cred_init) do not have to be +unsubscribed from as they are automatically removed. + +\subsection pi_pt_cred_other Other Notes + +Since credential managers may receive privileged information, the +signature requirements for credential managers are specially strict. + +\section pi_pt_conf Configuration Provider + +Provides configuration information. +[TODO: fill in] + +\subsection pi_pt_conf_comm Communication +[TODO: fill in] + +\subsection pi_pt_conf_init Initialization +[TODO: fill in] + +\subsection pi_pt_conf_exit Uninitialization +[TODO: fill in] + +\subsection pi_pt_conf_other Other Notes + +*/ + diff --git a/src/windows/identity/doc/plugin_structure.h b/src/windows/identity/doc/plugin_structure.h new file mode 100644 index 000000000..8c57b0300 --- /dev/null +++ b/src/windows/identity/doc/plugin_structure.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/*! + +\page pi_structure Structure of a module + +A NetIDMgr module is essentially a dynamically loadable library with a +specific set of exported symbols. Each export symbol and general +notes about writing a plugin module are documented below. + +\section pi_str_init Initialization + +Do not use DllMain or other system specific callback routines to +perform intilization tasks other than creating mutexes, initializing +thread local storage and other tasks that must be performed at that +stage. Specifically, do not call any NetIDMgr API functions from +within DllMain. + +\section pi_str_cb Callbacks + +The callbacks that must be implemented by a module are: + +- init_module() +- exit_module() + + */ diff --git a/src/windows/identity/doc/stylesheet.css b/src/windows/identity/doc/stylesheet.css new file mode 100644 index 000000000..fc3e0624d --- /dev/null +++ b/src/windows/identity/doc/stylesheet.css @@ -0,0 +1,271 @@ +BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV { + font-family: Geneva, Arial, Helvetica, sans-serif; +} +H1 { + text-align: center; +} +CAPTION { font-weight: bold } +DIV.qindex { + width: 100%; + background-color: #000000; + border: 1px solid #000000; + margin: 2px; + padding: 2px; + line-height: 100%; + color: #ffffff +} +DIV.nav { + width: 100%; + background-color: #000000; + border: 1px solid #000000; + text-align: center; + margin: 2px; + padding: 2px; + line-height: 100%; + color: #ffffff; +} +A.qindex { + text-decoration: none; + color: #ffffff; +} +A.qindex:visited { + text-decoration: none; + color: #ffffff; +} +A.qindex:hover { + text-decoration: none; + background-color: #ffffff; + color: #000000 +} +A.qindexHL { + text-decoration: none; + font-weight: bold; +} +A.qindexHL:hover { + text-decoration: none; + background-color: #333333; + color: #ffffff; +} +A.qindexHL:visited { text-decoration: none; background-color: #333333; color: #ffffff } +A.el { text-decoration: none; } +A.elRef { } +A.code:link { text-decoration: none; font-weight: normal; color: #0000FF} +A.code:visited { text-decoration: none; font-weight: normal; color: #0000FF} +A.codeRef:link { font-weight: normal; color: #0000FF} +A.codeRef:visited { font-weight: normal; color: #0000FF} +A:hover { text-decoration: none; background-color: #cccccc } +DL.el { margin-left: -1cm } +.fragment { + font-family: monospace +} +PRE.fragment { + border: 1px solid #CCCCCC; + background-color: #f5f5f5; + margin-top: 4px; + margin-bottom: 4px; + margin-left: 2px; + margin-right: 8px; + padding-left: 6px; + padding-right: 6px; + padding-top: 4px; + padding-bottom: 4px; +} +DIV.ah { background-color: black; font-weight: bold; color: #ffffff; margin-bottom: 3px; margin-top: 3px } +TD.md { background-color: #cccccc; font-weight: bold; } +TD.mdname1 { background-color: #cccccc; font-weight: bold; color: #000000; } +TD.mdname { background-color: #cccccc; font-weight: bold; color: #000000; width: 600px; } +DIV.groupHeader { + margin-left: 16px; + margin-top: 12px; + margin-bottom: 6px; + font-weight: bold; +} +DIV.groupText { margin-left: 16px; font-style: italic; font-size: 14px } +BODY { + background: white; + color: black; + margin-right: 20px; + margin-left: 20px; +} +TD.indexkey { + background-color: #cccccc; + font-weight: bold; + padding-right : 10px; + padding-top : 2px; + padding-left : 10px; + padding-bottom : 2px; + margin-left : 0px; + margin-right : 0px; + margin-top : 2px; + margin-bottom : 2px; + border: 1px solid #CCCCCC; +} +TD.indexvalue { + background-color: #cccccc; + font-style: italic; + padding-right : 10px; + padding-top : 2px; + padding-left : 10px; + padding-bottom : 2px; + margin-left : 0px; + margin-right : 0px; + margin-top : 2px; + margin-bottom : 2px; + border: 1px solid #CCCCCC; +} +TR.memlist { + background-color: #f0f0f0; +} +P.formulaDsp { text-align: center; } +IMG.formulaDsp { } +IMG.formulaInl { vertical-align: middle; } +SPAN.keyword { color: #008000 } +SPAN.keywordtype { color: #604020 } +SPAN.keywordflow { color: #e08000 } +SPAN.comment { color: #800000 } +SPAN.preprocessor { color: #806020 } +SPAN.stringliteral { color: #002080 } +SPAN.charliteral { color: #008080 } +.mdTable { + border: 1px solid #cccccc; + background-color: #cccccc; +} +.mdRow { + padding: 8px 10px; +} +.mdescLeft { + padding: 0px 8px 4px 8px; + font-size: 12px; + font-style: italic; + background-color: #FAFAFA; + border-top: 1px none #E0E0E0; + border-right: 1px none #E0E0E0; + border-bottom: 1px none #E0E0E0; + border-left: 1px none #E0E0E0; + margin: 0px; +} +.mdescRight { + padding: 0px 8px 4px 8px; + font-size: 12px; + font-style: italic; + background-color: #FAFAFA; + border-top: 1px none #E0E0E0; + border-right: 1px none #E0E0E0; + border-bottom: 1px none #E0E0E0; + border-left: 1px none #E0E0E0; + margin: 0px; +} +.memItemLeft { + padding: 1px 0px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: solid; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 12px; +} +.memItemRight { + padding: 1px 8px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: solid; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 13px; +} +.memTemplItemLeft { + padding: 1px 0px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: none; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 12px; +} +.memTemplItemRight { + padding: 1px 8px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: none; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 13px; +} +.memTemplParams { + padding: 1px 0px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: solid; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + color: #606060; + background-color: #FAFAFA; + font-size: 12px; +} +.search { color: #003399; + font-weight: bold; +} +FORM.search { + margin-bottom: 0px; + margin-top: 0px; +} +INPUT.search { font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #ffcc99; +} +TD.tiny { font-size: 75%; +} +a { + color: #0000ff; +} +a:visited { + color: #0000ff; +} +.anchor { + color: #000000; +} \ No newline at end of file diff --git a/src/windows/identity/doc/ui_actions.h b/src/windows/identity/doc/ui_actions.h new file mode 100644 index 000000000..ab3848ed3 --- /dev/null +++ b/src/windows/identity/doc/ui_actions.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/*! \page khui_actions Actions + + */ diff --git a/src/windows/identity/doc/ui_context.h b/src/windows/identity/doc/ui_context.h new file mode 100644 index 000000000..8ef325049 --- /dev/null +++ b/src/windows/identity/doc/ui_context.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/*! \page khui_context Contexts + + \section khui_context_contents Contents + + - \ref khui_context_intro "Introduction" + - \subpage khui_context_using + + \section khui_context_intro Introduction + + Several ::KMSG_CRED messages and many messages depend on the + selections that the user has made on the user interface. The UI + context functions and data structures provide access to this + information. + + The NetIDMgr user interface presents an outline view of all the + credentials that were provided by credentials providers. This + view consists of headers representing the outline levels and rows + representing individual credentials. + + Users can make multiple selections of credentials or headers from + this view. If all the credentials and subheaders under a + particular outline level are selected, then the header itself is + automatically selected. There may be multiple disjointed + selections of headers and credentials. + + In addition, the current cursor position also acts as a selector. + The credential or header under the cursor may not actually be + selected. The cursor is not the mouse pointer, but the focus + rectangle that may be moved either using the keyboard or by + clicking on a credential or header. + + Thus there are two independent groups of selections: + + - Credentials and headers which are in a selected state at some + specific point in time (the current selection). + + - The current credential or selection which the cursor is on (the + cursor selection). + + There are a few notes on how credentials are selected: + + - An "empty" header (a header that does not contain any credential + rows) does not appear in a UI context. However they can appear + as the current cursor context. + + - At its current implementation, cursor selections of identity, + credential type, and individual credentials are treated as + special cases since they are the most common. + + How the UI context is used when processing a specific action or + message depends on the action or message. If an action operates + on a group of credentials, then the current selection may be used, + and on the other hand if an action or message relates to just one + credential, identity or credential type is invoked, then the + cursor selection is invoked. + + For example, double-clicking a credential, or right clicking and + selecting 'Properties' from the context menu launches the property + window for a credential. This operates on the cursor selection + since that reflects where the user double clicked. However, + choosing 'Destroy' from the context menu invokes a command that + can be applied to a group of credential, and hence uses the + current selection. + + Next: \ref khui_context_using "Using Contexts" + */ + +/*! \page khui_context_using Using Contexts + + \section khui_context_using_1 Obtaining the context + + Typically, messages sent by actions that rely on UI context will + obtain and store the context in a location that is accessible to + the handlers of the message. + + If a plugin needs to obtain the UI context, it should do so by + calling khui_context_get() and passing in a pointer to a + ::khui_action_context structure. + + Once obtained, the contents of the ::khui_action_context structure + should be considered read-only. When the plugin is done with the + structure, it should call ::khui_context_release(). This cleans + up any additional memory allocated for storing the context as well + as releasing all the objects that were referenced from the + context. + + \section khui_context_sel_ctx Selection context + + The selection context is specified in the ::khui_action_context + structure in the \a sel_creds and \a n_sel_creds fields. These + combined provide an array of handles to credentials which are + selected. + + \note If \a n_sel_creds is zero, then \a sel_creds may be NULL. + + \section khui_context_cur_ctx Cursor context + + The scope of the cursor context is specified in the \a scope field + of the ::khui_action_context strucutre. The scope can be one of: + + - ::KHUI_SCOPE_NONE + - ::KHUI_SCOPE_IDENT + - ::KHUI_SCOPE_CREDTYPE + - ::KHUI_SCOPE_GROUP + - ::KHUI_SCOPE_CRED + + Depending on the scope, several other members of the strucre may + also be set. + + In general, the cursor context can be a single credential or an + entire outline level. Unlike the selection context, since this + specifies a single point of selection it can not be disjointed. + + The contents of the \a identity, \a cred_type, \a cred, \a headers + and \a n_headers are described in the documentation of each of the + scope values above. + + \subsection khui_context_sel_ctx_grp KHUI_SCOPE_GROUP + + The ::KHUI_SCOPE_GROUP scope is the generic scope which describes + a cursor selection that can not be simplified into any other + scope. + + In this case, the selection is described by an array of + ::khui_header elements each of which specify a criterion for + narrowing down the selection of credentials. The ::khui_header + structure specifies an attribute in the \a attr_id field and a + value in the \a data and \a cb_data fields. The credentials that + are selected are those in the root credential set whose repective + attributes contain the values specified in each of the + ::khui_header elements. + + For example, the following selection: + + \image html credview-select-outline.jpg + + will result in the following header specification: + + \code + ctx.n_headers = 3; + + ctx.headers[0].attr_id = KCDB_ATTR_LOCATION; + ctx.headers[0].data = L"grailauth@KHMTEST"; + ctx.headers[0].cb_data = sizeof(L"grailauth@KHMTEST"); + + ctx.headers[1].attr_id = KCDB_ATTR_ID; + ctx.headers[1].data = &handle_to_identity; + ctx.headers[1].cb_data = sizeof(khm_handle); + + ctx.headers[2].attr_id = KCDB_ATTR_TYPE; + ctx.headers[2].data = &kerberos_5_credtype; + ctx.headers[2].cb_data = sizeof(khm_int32); + \endcode + + \note The attribute that is used to specify the header is not the + display attribute, but the canonical attribute. For example, + in the above, the second header was actually + KCDB_ATTR_ID_NAME. But KCDB_ATTR_ID was used since that is + the canonical source for KCDB_ATTR_ID_NAME. See ::kcdb_attrib + for more information on canonical attributes. +*/ diff --git a/src/windows/identity/doc/ui_main.h b/src/windows/identity/doc/ui_main.h new file mode 100644 index 000000000..0f9ab661c --- /dev/null +++ b/src/windows/identity/doc/ui_main.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/*! \page khui User Interface Topics + + \section khui_contents Contents + + - \subpage khui_actions + - \subpage khui_menus + - \subpage khui_context + */ diff --git a/src/windows/identity/doc/ui_menus.h b/src/windows/identity/doc/ui_menus.h new file mode 100644 index 000000000..c7a95a364 --- /dev/null +++ b/src/windows/identity/doc/ui_menus.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/*! \page khui_menus Menus + + */ diff --git a/src/windows/identity/help/Index.hhk b/src/windows/identity/help/Index.hhk new file mode 100644 index 000000000..2e24f6f3e --- /dev/null +++ b/src/windows/identity/help/Index.hhk @@ -0,0 +1,9 @@ + + + + + + +
    +
+ diff --git a/src/windows/identity/help/Makefile b/src/windows/identity/help/Makefile new file mode 100644 index 000000000..2b823d85a --- /dev/null +++ b/src/windows/identity/help/Makefile @@ -0,0 +1,36 @@ +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +MODULE=help +!include <..\config\Makefile.w32> + +CHMFILE=$(DOCDIR)\netidmgr.chm + +INCFILES=$(INCDIR)\khhelp.h + +all: mkdirs $(CHMFILE) $(INCFILES) + +$(CHMFILE): netidmgr.hhp + -hhc netidmgr.hhp + $(CP) netidmgr.chm $(CHMFILE) diff --git a/src/windows/identity/help/html/images/Thumbs.db b/src/windows/identity/help/html/images/Thumbs.db new file mode 100644 index 0000000000000000000000000000000000000000..01828e4debfe252e9da4b4bd28fd7122f9d4b540 GIT binary patch literal 3584 zcmca`Uhu)fjZzO8(10BSGsD0CoD6J8;*3Bx2!nwD0|OI~0pkDr|NlQkkbwcn90fxt z1pWfu3W`4vW&uVbD>OcbkQYXsN;Sr0E8 zaOw0235dIQl#v-Ie*l!&fWQz!kxjBNFfoGc2bD!2nplJCp!Pd6Bm#Y%$dCgJg>+&y zBdHSrmVzLAVE{=ACsPP0V}etbbP1T=vt72p@5MI=teen4o)s^pn|Qy9LmVd%*4VBay3wOEl{3;MUYiU(a@1i zI53f2sZhkIapFP_Wv7h?MT0JWP%%y_YU1P)6PJ*bQdLve(9|+9H8Z!cv~qTFb#wRd z^a>6M4GWKmj7m;PO-s+n%qlJ^Ei136tZHs)ZENr7?3y%r%G7DoXUv?nXz`Mz%a*TL zxoXqqEnBy3-?4Mop~FXx9y@;Gy|miew|{A5 zR#ocTeT$dfJ2c&F`?N`GHk=n;kup)%lV{Bmfddm9S;8OBe|z{*ul=L?qxl`v{~2xE z_2GC|NpAT+!S#GwAK9IMb30k=?Lp-`f&VV5zDV;unztyVGSp$+;s^g3dUXG&mp+ca z`RMlXeKJ3yKiof3C%AL>&TD%rmmZt!TU_K~w1qEE*I4^-!Rp_4fBUvwl;`u`9<3j; z>6OL#`guk3`o!8*Z0A1bnsd%L=4v8e$j-%jr7CkRqn~9wzmok-{^rN!s@f-G*UBq} zXTG_5EB1Z$(&VrEx<322TW$T;|5&&9z@l%DkNk4$<|#V7)>qrxZe9$Vui3Y7%UOle zDxC`YnynM=v=)nepEo^D@yqd(`?re!5&rJ_cfmfzTQv?BYXa_Zew>oYVlHepk!@jW z*~H4(DU+Cz)8_LERrV?~%4qDi2-;hIX#S1Ehwk&%@mKt3I4Jz;SKHS5$3OIhkIYlb z$bI|ndF9(4BM!6U9gl)Ody4o!tIRe2bi1#vWzp_c$%UdTomxHb8HUAqy}kAO)3V!M z-+Z@Cy>0l--`1T>%-^wUORx6s-QtrXRf^&w zV{hJlQgrU!n=@z5cwSr7_0Y`z|4mRG!VWHffJF_cxM5HMVsK8*J`fap`v_wWLng3Y UlFgvU;LhO5;0BZ(1=I`y0HhmucmMzZ literal 0 HcmV?d00001 diff --git a/src/windows/identity/help/html/images/link.GIF b/src/windows/identity/help/html/images/link.GIF new file mode 100644 index 0000000000000000000000000000000000000000..1af792f08de91b7f8d08e6103150793397f6bc67 GIT binary patch literal 932 zcmb`Gy=#?W48`LY)X8F7XrUrS5Wlu4R5~ft+D;A*DqX}992|6TbGWq>hbl0iMnWrW))4vOw7eXEUVX5+{9fx#Ir_2 zB}~F4LZX;Ts-#J}WI(%s3mPnj>CW0yVXCegs#$Bv%+y>h)UsGmbW?ZrP|qSoZ#b5&s7QcmdxEe%(LXef?~KuSS0Ep zMX#k>h9&aw1DZ@@2l^p_LRbR^X;{(<4;F(BG+0^ARhgU!_bemP$6B%okHlBx!h)h_ zc;X{YV*tEp$7s}IC4NAYY3zU#f>8)-z#z>ewBW&Fuz}7xQFT)$Cz6X{B>GrO7VRY~ zU)#N_*iKkBx2sfdGrC-F-qmQcqmIxN7E&W!H)%WyxIyzea3#z`6RU>)Y~3W%Er;r@ zuK$E|O-pVcJh}K_b*8K5z~tKE!#6j&_e|V-)jd1f+q>=DrPb%Nqu2X)Eqwpv?k&#${CZtm+@IJNxZ(fZu(e0O+we(%`*k*n)}mp2~2pE-79Y3i(A+?dw!jkg28 q_Me`=cK3MS`sJy)CkHQl`}yO}*E7#{T(QXyOHaSN+_`15jQj)aZkRg& literal 0 HcmV?d00001 diff --git a/src/windows/identity/help/html/khm.css b/src/windows/identity/help/html/khm.css new file mode 100644 index 000000000..82c4d57f7 --- /dev/null +++ b/src/windows/identity/help/html/khm.css @@ -0,0 +1,13 @@ +BODY { font-family:helvetica,sans-serif; + font-size:8pt; + font-style:normal; + background-color:white; } + +H1 { font-size: 10pt; + border:solid 1px black; + padding:5px; + background-color:lightgrey + } + +H2 { } + diff --git a/src/windows/identity/help/html/menu_exit.htm b/src/windows/identity/help/html/menu_exit.htm new file mode 100644 index 000000000..2130df192 --- /dev/null +++ b/src/windows/identity/help/html/menu_exit.htm @@ -0,0 +1,9 @@ + + + title + + + + + + \ No newline at end of file diff --git a/src/windows/identity/help/html/menu_file.htm b/src/windows/identity/help/html/menu_file.htm new file mode 100644 index 000000000..021f71f5a --- /dev/null +++ b/src/windows/identity/help/html/menu_file.htm @@ -0,0 +1,18 @@ + + + File menu + + + + + +

File menu

+ +

Menu items

+ + + + \ No newline at end of file diff --git a/src/windows/identity/help/html/menu_properties.htm b/src/windows/identity/help/html/menu_properties.htm new file mode 100644 index 000000000..2130df192 --- /dev/null +++ b/src/windows/identity/help/html/menu_properties.htm @@ -0,0 +1,9 @@ + + + title + + + + + + \ No newline at end of file diff --git a/src/windows/identity/help/html/template.htm b/src/windows/identity/help/html/template.htm new file mode 100644 index 000000000..2130df192 --- /dev/null +++ b/src/windows/identity/help/html/template.htm @@ -0,0 +1,9 @@ + + + title + + + + + + \ No newline at end of file diff --git a/src/windows/identity/help/html/welcome.htm b/src/windows/identity/help/html/welcome.htm new file mode 100644 index 000000000..32d7d05b2 --- /dev/null +++ b/src/windows/identity/help/html/welcome.htm @@ -0,0 +1,24 @@ + + + Welcome to Khimaira + + + + + +

Welcome to Khimaira

+ +

Khimaira is a credentials manager that lets you manage Kerberos, +AFS and other types of credentials. +

+ +

The following web sites provide more information about Kerberos and +AFS:

+ + + + + \ No newline at end of file diff --git a/src/windows/identity/help/khhelp.h b/src/windows/identity/help/khhelp.h new file mode 100644 index 000000000..4ffa6d8f5 --- /dev/null +++ b/src/windows/identity/help/khhelp.h @@ -0,0 +1,23 @@ + +#define IDH_WELCOME 1000 +#define IDH_MENU_FILE 1001 +#define IDH_MENU_CRED 1002 +#define IDH_MENU_VIEW 1003 +#define IDH_MENU_OPTIONS 1004 +#define IDH_MENU_HELP 1005 + +#define IDH_ACTION_PROPERTIES 2000 +#define IDH_ACTION_EXIT 2001 +#define IDH_ACTION_NEW_ID 2002 +#define IDH_ACTION_SET_DEF_ID 2003 +#define IDH_ACTION_SET_SRCH_ID 2004 +#define IDH_ACTION_DESTROY_ID 2005 +#define IDH_ACTION_RENEW_ID 2006 +#define IDH_ACTION_PASSWD_ID 2007 +#define IDH_ACTION_NEW_CRED 2008 +#define IDH_ACTION_CHOOSE_COLS 2009 +#define IDH_ACTION_DEBUG_WINDOW 2010 +#define IDH_ACTION_VIEW_REFRESH 2011 +#define IDH_ACTION_OPT_KHIM 2012 +#define IDH_ACTION_OPT_INIT 2013 +#define IDH_ACTION_OPT_NOTIF 2014 diff --git a/src/windows/identity/help/netidmgr.hhp b/src/windows/identity/help/netidmgr.hhp new file mode 100644 index 000000000..8e0d5a597 --- /dev/null +++ b/src/windows/identity/help/netidmgr.hhp @@ -0,0 +1,21 @@ +[OPTIONS] +Auto Index=Yes +Compatibility=1.1 or later +Compiled file=netidmgr.chm +Contents file=toc.hhc +Default topic=html/welcome.htm +Display compile progress=No +Index file=Index.hhk +Language=0x409 English (United States) +Title=NetIDMgr + + +[MAP] +#include khhelp.h + +[INFOTYPES] +Category:Concepts +CategoryDesc:Authentication, authorization and related concepts. +Category:Usage +CategoryDesc:Usage instructions for NetIDMgr + diff --git a/src/windows/identity/help/toc.hhc b/src/windows/identity/help/toc.hhc new file mode 100644 index 000000000..cde5119a3 --- /dev/null +++ b/src/windows/identity/help/toc.hhc @@ -0,0 +1,47 @@ + + + + + + + + + + + + + +
    +
  • + + + +
  • + + +
      +
    • + + +
        +
      • + + + +
      +
    • + + +
        +
      • + + + +
      • + + + +
      +
    +
+ diff --git a/src/windows/identity/include/Makefile b/src/windows/identity/include/Makefile new file mode 100644 index 000000000..17182d57a --- /dev/null +++ b/src/windows/identity/include/Makefile @@ -0,0 +1,37 @@ +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +MODULE=include +!include <../config/Makefile.w32> + +INCFILES= \ + $(INCDIR)\khdefs.h \ + $(INCDIR)\kherror.h \ + $(INCDIR)\khlist.h \ + $(INCDIR)\khmsgtypes.h + +all: $(INCFILES) + +clean:: + $(RM) $(INCFILES) diff --git a/src/windows/identity/include/khdefs.h b/src/windows/identity/include/khdefs.h new file mode 100644 index 000000000..427926306 --- /dev/null +++ b/src/windows/identity/include/khdefs.h @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KHDEFS_H__ +#define __KHIMAIRA_KHDEFS_H__ + +/*! \defgroup khdef Core definitions + + Key type definitions used throughout NetIDMgr. + */ +/*@{*/ +#include +#include +#include + +/*!\typedef khm_octet + \brief A byte (8 bit unsigned)*/ + +/*!\typedef khm_int16 + \brief A signed 16 bit quantity */ + +/*!\typedef khm_ui_2 + \brief An unsigned 16 bit quantity */ + +/*!\typedef khm_int32 + \brief A signed 32 bit quantity */ + +/*!\typedef khm_ui_4 + \brief An unsigned 32 bit quantity */ + +/*!\typedef khm_int64 + \brief A signed 64 bit quantity */ + +/*!\typedef khm_ui_8 + \brief An unsigned 64 bit quantity */ + +typedef unsigned __int8 khm_octet; + +typedef __int16 khm_int16; +typedef unsigned __int16 khm_ui_2; + +typedef __int32 khm_int32; +typedef unsigned __int32 khm_ui_4; + +typedef __int64 khm_int64; +typedef unsigned __int64 khm_ui_8; + +#define VALID_INT_BITS INT_MAX +#define VALID_UINT_BITS UINT_MAX + +#define KHM_UINT32_MAX 4294967295 + +#define KHM_INT32_MAX 2147483647 +/* this strange form is necessary since - is a unary operator, not a sign + indicator */ +#define KHM_INT32_MIN (-KHM_INT32_MAX-1) + +#define KHM_UINT16_MAX 65535 + +#define KHM_INT16_MAX 32767 +/* this strange form is necessary since - is a unary operator, not a sign + indicator */ +#define KHM_INT16_MIN (-KHM_INT16_MAX-1) + +/*! \brief Generic handle type. + + Handles in NetIDMgr are generic pointers. +*/ +typedef void * khm_handle; + +/*! \brief The invalid handle + + Just used to indicate that this handle does not point to anything useful. + Usually returned by a function that returns a handle as a signal that the + operation failed. +*/ +#define KHM_INVALID_HANDLE ((khm_handle) NULL) + +/*! \brief Boolean. +*/ +typedef khm_int32 khm_boolean; + +/*! \brief A size + */ +typedef size_t khm_size; + +/*! \typedef ssize_t + \brief Signed size specifier + + Just a signed version of size_t + */ + +#ifdef _WIN64 +typedef __int64 ssize_t; +#else +typedef _W64 int ssize_t; +#endif + +typedef ssize_t khm_ssize; + +#if defined(_WIN64) +typedef unsigned __int64 khm_wparm; +/*TODO: is this enough? */ +typedef unsigned __int64 khm_lparm; +#elif defined(_WIN32) +typedef unsigned __int32 khm_wparm; +typedef unsigned __int64 khm_lparm; +#else +#error khm_wparm and khm_lparm need to be defined for this platform +#endif + +/*!\def KHMAPI + \brief Calling convention for NetIDMgr exported functions + + The caling convention for all NetIDMgr exported functions is \b + __stdcall , unless otherwise noted. + */ + +/*!\def KHMEXP + \brief Export prefix for NetIDMgr exported functions + + When compiling source that exports functions, those exported + function declarations will be done as follows: + + \code + __declspec(dllexport) khm_int32 __stdcall function_name(arguments...); + \endcode + + This eliminates the need for a separate exports definition file. + However, it doesn't preserve ordinals, but we aren't guaranteeing + that anyway. + + On the other hand, if a particular function is going to be imported + from a DLL, it should declared as follows: + + \code + __declspec(dllimport) khm_int32 __stdcall function_name(arguments...); + \endcode + + This allows the compiler to properly instrument the import. If the + function is not declared this way, there will be a stub function + generated that will just jump to the proper import, generating + redundant instructions and wasting execution time. + + This macro encapsulates the proper declaration specifier. + */ + +#ifdef _WIN32 +#define KHMAPI __stdcall + +#define KHMEXP_EXP __declspec(dllexport) +#define KHMEXP_IMP __declspec(dllimport) + +#define KHMEXP KHMEXP_EXP +#endif + +/* Generic permission values */ +/*! \brief Generic read permission or request */ +#define KHM_PERM_READ 0x100 + +/*! \brief Generic write permission or request */ +#define KHM_PERM_WRITE 0x200 + +/* Generic flags */ +/*! \brief Generic create request + + For most lookup functions, specifying this flag indicates that if + the requested object is not found it should be created. +*/ +#define KHM_FLAG_CREATE 0x1000 + +/*! \brief Wrap to DWORD boundary + + Returns the smallest integer greater than or equal to the + parameter that is a multiple of 4. + + \note Only use with positive integers. */ +#define UBOUND32(d) ((((d)-1)&~3) + 4) + +/*! \brief Offset a pointer by a number of bytes + + Given a pointer, returns a void pointer that is a given number of + bytes offset from the pointer. + */ +#define BYTEOFFSET(p,off) ((void *)(((char *) (p)) + (off))) + +/*! \brief Check for powers of 2 + + Return TRUE if the operand is a positive power of 2 or 0*/ +#define IS_POW2(d) ((d)>=0 && !((d) & ((d) - 1))) + +/*! \brief Wrap to upper bound based on start and step size + + Return the smallest element in the series s, s+t, s+2*t, + s+3*t, ... that is greater than or equal to \c v. +*/ +#define UBOUNDSS(v,start,step) (((v)<=(start))?(start):(start)+((((v)-((start)+1))/(step))+1)*(step)) + +/* \brief Length of an array +*/ +#define ARRAYLENGTH(x) (sizeof(x)/sizeof(x[0])) + +/*! \brief Generic version type*/ +typedef struct tag_khm_version { + khm_ui_2 major; /*!< Major version number */ + khm_ui_2 minor; /*!< Minor version number */ + khm_ui_2 patch; /*!< Patch level */ + khm_ui_2 aux; /*!< Auxilary level (usually carries a build number) */ +} khm_version; + +/*@}*/ +#endif diff --git a/src/windows/identity/include/kherror.h b/src/windows/identity/include/kherror.h new file mode 100644 index 000000000..d56fa7dc7 --- /dev/null +++ b/src/windows/identity/include/kherror.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/* Exported */ +#ifndef __KHIMAIRA_KHERROR_H +#define __KHIMAIRA_KHERROR_H + +/*! \defgroup kherror NetIDMgr errors + +@{*/ +/*! \brief Base for error codes + + NetIDMgr errors range from \a KHM_ERROR_BASE to KHM_ERROR_BASE + + KHM_ERROR_RANGE, with the exception of KHM_ERROR_SUCCESS and + KHM_ERROR_NONE. + */ +#define KHM_ERROR_BASE 0x40000000L + +/*! \brief Range for error codes + + NetIDMgr errors range from \a KHM_ERROR_BASE to + KHM_ERROR_BASE + KHM_ERROR_RANGE. +*/ +#define KHM_ERROR_RANGE 256L + +/*! \defgroup kherror_codes Error codes + @{*/ + +/*! \brief No error */ +#define KHM_ERROR_NONE 0x00000000L + +/*! \brief Success. Same as \a KHM_ERROR_NONE */ +#define KHM_ERROR_SUCCESS KHM_ERROR_NONE + +/*! \brief The supplied name was invalid */ +#define KHM_ERROR_INVALID_NAME (KHM_ERROR_BASE + 1) + +/*! \brief Too much data + + A supplied buffer was invalid, was of insufficient size, or a + buffer was of a larger size than expected + */ +#define KHM_ERROR_TOO_LONG (KHM_ERROR_BASE + 2) + +/*! \brief One or more parameters supplied to a function were invalid */ +#define KHM_ERROR_INVALID_PARM (KHM_ERROR_BASE + 3) + +/*! \brief A duplicate. + + Usually means that something that should have been unique was + found to be not. + */ +#define KHM_ERROR_DUPLICATE (KHM_ERROR_BASE + 4) + +/*! \brief An object was not found + + An object referenced in a parameter was not found. + */ +#define KHM_ERROR_NOT_FOUND (KHM_ERROR_BASE + 5) + +/*! \brief The relevant subsystem is not ready + + Indicates that initialization has not been completed for a + subsystem. + */ +#define KHM_ERROR_NOT_READY (KHM_ERROR_BASE + 6) + +/*! \brief No more resources + + A limited resource has been exhausted. + */ +#define KHM_ERROR_NO_RESOURCES (KHM_ERROR_BASE + 7) + +/*! \brief Type mismatch + */ +#define KHM_ERROR_TYPE_MISMATCH (KHM_ERROR_BASE + 8) + +/*! \brief Already exists + + Usually indicates that an exclusive create operation failed due to + the existence of a similar object. Subtly different from + ::KHM_ERROR_DUPLICATE + */ +#define KHM_ERROR_EXISTS (KHM_ERROR_BASE + 9) + +/*! \brief Operation timed out + */ +#define KHM_ERROR_TIMEOUT (KHM_ERROR_BASE + 10) + +/*! \brief An EXIT message was received + */ +#define KHM_ERROR_EXIT (KHM_ERROR_BASE + 11) + +/*! \brief Unknown or unspecified error + */ +#define KHM_ERROR_UNKNOWN (KHM_ERROR_BASE + 12) + +/*! \brief General error + */ +#define KHM_ERROR_GENERAL KHM_ERROR_UNKNOWN + +/*! \brief An index was out of bounds + */ +#define KHM_ERROR_OUT_OF_BOUNDS (KHM_ERROR_BASE + 13) + +/*! \brief Object already deleted + + One or more objects that were referenced were found to have been + already deleted. + */ +#define KHM_ERROR_DELETED (KHM_ERROR_BASE + 14) + +/*! \brief Invalid operation + + The operation was not permitted to continue for some reason. + Usually because the necessary conditions for the operation haven't + been met yet or the operation can only be performed at certain + times during the execution of NetIDMgr. + */ +#define KHM_ERROR_INVALID_OPERATION (KHM_ERROR_BASE + 15) + +/*! \brief Signature check failed + */ +#define KHM_ERROR_INVALID_SIGNATURE (KHM_ERROR_BASE + 16) + +/*! \brief Not implemented yet + + The operation that was attempted involved invoking functionality + that has not been implemented yet. + */ +#define KHM_ERROR_NOT_IMPLEMENTED (KHM_ERROR_BASE + 17) + +/*! \brief The objects were equivalent + */ +#define KHM_ERROR_EQUIVALENT (KHM_ERROR_BASE + 18) + +/*! \brief No provider exists to service the request +*/ +#define KHM_ERROR_NO_PROVIDER (KHM_ERROR_BASE + 19) + +/*! \brief The operation succeeded, but with errors +*/ +#define KHM_ERROR_PARTIAL (KHM_ERROR_BASE + 20) + +/*@}*/ /*kherror_codes*/ + +/*! \brief Tests whether a return value indicates success */ +#define KHM_SUCCEEDED(rv) ((rv)==KHM_ERROR_NONE) + +/*! \brief Tests whether a return value indicates failure */ +#define KHM_FAILED(rv) ((rv)!=KHM_ERROR_NONE) + +/*@}*/ +#endif diff --git a/src/windows/identity/include/khlist.h b/src/windows/identity/include/khlist.h new file mode 100644 index 000000000..330cfc498 --- /dev/null +++ b/src/windows/identity/include/khlist.h @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/* Not exported */ +#ifndef _KHIMAIRA_KHLIST_H +#define _KHIMAIRA_KHLIST_H + +/* Note that most of these are "unsafe" macros. Not for general use */ + +/* LIFO lists */ +#define LDCL(type) \ + type * next; \ + type * prev + +#define LINIT(pe) \ + do { \ + (pe)->next = NULL; \ + (pe)->prev = NULL; } while(0) + +#define LPUSH(pph,pe) \ + do { \ + (pe)->next = *pph; \ + (pe)->prev = NULL; \ + if(*(pph)) (*(pph))->prev = (pe); \ + (*(pph)) = (pe); } while(0) + +#define LPOP(pph,ppe) \ + do { \ + *(ppe) = *(pph); \ + if(*(pph)) *(pph) = (*(pph))->next; \ + if(*(pph)) (*(pph))->prev = NULL; \ + if(*(ppe)) (*(ppe))->next = NULL; \ + } while(0) + +#define LDELETE(pph,pe) \ + do { \ + if((pe)->prev) (pe)->prev->next = (pe)->next; \ + if((pe)->next) (pe)->next->prev = (pe)->prev; \ + if(*(pph) == (pe)) *(pph) = (pe)->next; \ + (pe)->next = (pe)->prev = NULL; \ + } while(0) + +#define LEMPTY(pph) (*(pph) == NULL) + +#define LNEXT(pe) ((pe)?(pe)->next:NULL) + +#define LPREV(pe) ((pe)?(pe)->prev:NULL) + +/* Trees with LIFO child lists */ +#define TDCL(type) \ + LDCL(type); \ + type * children; \ + type * parent + +#define TINIT(pe) \ + do { \ + (pe)->children = NULL; \ + (pe)->parent = NULL; } while(0) + +#define TADDCHILD(pt,pe) \ + do { \ + LPUSH(&((pt)->children),(pe)); \ + (pe)->parent = (pt); } while(0) + +#define TFIRSTCHILD(pt) ((pt)?(pt)->children:NULL) + +#define TPOPCHILD(pt, ppe) \ + do { \ + LPOP(&((pt)->children), ppe); \ + if(*(ppe)) (*(ppe))->parent = NULL; \ + } while(0) + +#define TDELCHILD(pt, pe) \ + do { \ + LDELETE(&((pt)->children), (pe)); \ + (pe)->parent = NULL; } while(0) + +#define TPARENT(pe) ((pe)?(pe)->parent:NULL) + +/* FIFO lists */ +#define QDCL(type) \ + type * head; \ + type * tail + +#define QINIT(pq) \ + do { \ + (pq)->head = (pq)->tail = NULL; \ + } while(0) + +#define QPUT(pq, pe) \ + do { \ + LPUSH(&(pq)->tail, (pe)); \ + if(!(pq)->head) (pq)->head = (pe); \ + } while(0) + +#define QGET(pq, ppe) \ + do { \ + *(ppe) = (pq)->head; \ + if(*(ppe)) { \ + (pq)->head = (*(ppe))->prev; \ + if( (*(ppe))->prev ) (*(ppe))->prev->next = NULL; \ + (*(ppe))->prev = NULL; \ + if( (pq)->tail == *(ppe)) (pq)->tail = NULL; \ + } \ + } while(0) + +#define QDEL(pq, pe) \ + do { \ + if((pq)->head == (pe)) (pq)->head = LPREV(pe); \ + LDELETE(&((pq)->tail), (pe)); \ + } while(0) + + +#define QGETT(pq,ppe) \ + do { \ + *(ppe) = (pq)->tail; \ + if(*(ppe)) { \ + (pq)->tail = (*(ppe))->next; \ + if( (*(ppe))->next ) (*(ppe))->next->prev = NULL; \ + (*(ppe))->next = NULL; \ + if( (pq)->head == *(ppe)) (pq)->head = NULL; \ + } \ + } while(0) + +#define QTOP(pq) ((pq)->head) +#define QBOTTOM(pq) ((pq)->tail) +#define QNEXT(pe) ((pe)->prev) +#define QPREV(pe) ((pe)->next) + +/* Trees with FIFO child lists */ +#define TQDCL(type) \ + LDCL(type); \ + QDCL(type); \ + type * parent + +#define TQINIT(pe) \ + do { \ + QINIT(pe); \ + (pe)->parent = NULL; } while(0) + +#define TQADDCHILD(pt,pe) \ + do { \ + QPUT((pt), (pe)); \ + (pe)->parent = (pt); } while(0) + +#define TQFIRSTCHILD(pt) ((pt)?QTOP(pt):NULL) + +#define TQPARENT(pe) ((pe)?(pe)->parent:NULL) + +#endif diff --git a/src/windows/identity/include/khmsgtypes.h b/src/windows/identity/include/khmsgtypes.h new file mode 100644 index 000000000..8348bbf95 --- /dev/null +++ b/src/windows/identity/include/khmsgtypes.h @@ -0,0 +1,700 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KHMSGTYPES_H +#define __KHIMAIRA_KHMSGTYPES_H + +/*! \addtogroup kmq +@{*/ +/*! \defgroup kmq_msg Message Types +@{*/ + +/*! \name Global message types +@{*/ + +/*! \brief System messages. + + All subscribers are subscribed to the system message class by default. + + \see \ref kmq_msg_system +*/ +#define KMSG_SYSTEM 0 + +/*! \brief Ad-hoc messages. + + These are messages that are sent through add hoc publishers and + subscribers. +*/ +#define KMSG_ADHOC 1 + +/*! \brief NetIDMgr Credentials Database messages + + These messages notify subscribers of events related to the + credentials database, such as the registration, unregistration and + modification of identities, attributes, attribute types and + credential types. It also provides notifications of changes to + the root crednetial set. + + \see \ref kmq_msg_kcdb +*/ +#define KMSG_KCDB 2 + +/*! \brief NetIDMgr Module Manager messages + + \see \ref kmq_msg_kmm +*/ +#define KMSG_KMM 3 + +/*! \brief NetIDMgr Credential messages + + Notifications of crednetial events. These are the most important + events that a credentials provider should respond to. The + notifications provide co-oridination between credential providers + for performing basic credentials management tasks such as + obtaining new credentials for an identity, deleting credentials + for an identity, obtaining or deleting credentials of a particular + type for an identity etc. + + \see \ref cred_msgs + \see \ref kmq_msg_cred + */ +#define KMSG_CRED 4 + +/*! \brief Action list messages + + Notifications of changes in action state. + + \see \ref kmq_msg_act + */ +#define KMSG_ACT 5 + +/*! \brief Alert messages + + Notifier is the component which displays alerts and error messages + when the NetIDMgr window is normally in operation and which + displays balloon prompts when the window is minimized to alert the + user to important messages such as credentials expiring etc. + + \note This is an internal message class. Components that are not + the notifier should not be subscribing to alert messages. + + \see \ref kmq_msg_alert + */ +#define KMSG_ALERT 6 + +/*! \brief Identity messages + + These are messages that are sent to the identity provider. These + are generally dispatched through a specific subscription object + and are not broadcast. + + \see \ref kmq_msg_ident + */ +#define KMSG_IDENT 7 + +/*! \brief Base message type ID for customized message types + */ +#define KMSGBASE_USER 16 + +/*@}*/ + +/*! \defgroup kmq_msg_system KMSG_SYSTEM subtypes +@{*/ +/*! \brief Generic initialization message + + This message is used by specific components to signal that the + recipient is to perform initialization tasks. As a convention, + the recipient should return KHM_ERROR_SUCCESS if it successfully + performed the initlization tasks or some other value if it failed + to do so. Failure to successfully initialize is usually taken to + mean that the recipient component is not able to perform its + function. + + Usually this is the first message to be received by the recipient. + + \see \ref pi_pt_cred_init + */ +#define KMSG_SYSTEM_INIT 1 +/*! \brief Generic uninitialization message + + Used by specific components to signal that the recipient should + perform uninitilization tasks in preparation of termination. The + return value of this message is not used. + + Usually this is the last message to be received by the recipient. + + \see \ref pi_pt_cred_exit + */ +#define KMSG_SYSTEM_EXIT 2 + +/*! \brief Message completion + + This is an internal message + */ +#define KMSG_SYSTEM_COMPLETION 3 +/*@}*/ + +/*! \defgroup kmq_msg_kcdb KMSG_KCDB subtypes +@{*/ +#define KMSG_KCDB_IDENT 1 +#define KMSG_KCDB_CREDTYPE 2 +#define KMSG_KCDB_ATTRIB 3 +#define KMSG_KCDB_TYPE 4 + +/*! \brief Generic credentials request + + \see ::kcdb_cred_request for more information + */ +#define KMSG_KCDB_REQUEST 256 +/*@}*/ + +/*! \defgroup kmq_msg_kmm KMSG_KMM subtypes +@{*/ +#define KMSG_KMM_I_REG 1 + +#define KMSG_KMM_I_DONE 2 +/*@}*/ + +/*! \defgroup kmq_msg_act KMSG_ACT subtypes + @{*/ + +/*! \brief One or more actions changed state + + This message is sent in response to a call to + khui_enable_actions() or khui_enable_action() and indicates that + one or more actions have changed their state. + */ +#define KMSG_ACT_ENABLE 1 + +/*! \brief One or more actions changed check state + + Sent in response to khui_check_radio_action() or + khui_check_action() and indicates that one or more actions have + either been checked or unchecked. + */ +#define KMSG_ACT_CHECK 2 + +/*! \brief Refresh action states + + Sent after a batch of modifications were made to action states. + */ +#define KMSG_ACT_REFRESH 3 + +#define KMSG_ACT_BEGIN_CMDLINE 128 + +/*@}*/ + +/*! \defgroup kmq_msg_cred KMSG_CRED subtypes + @{*/ +/*! \brief Root credential set changed + + This message is issued when the root credential set successfully + collected credentials from another credential set. + + \a uparam of the message is set to a bitmask indicating the change + that occured. It is a combination of ::KCDB_DELTA_ADD, + ::KCDB_DELTA_DEL and ::KCDB_DELTA_MODIFY. + */ +#define KMSG_CRED_ROOTDELTA 1 + +/*! \brief Re-enumerate credentials + + A notice to all credential providers to re-enumerate their + respective credentials. + + \note May be sent to individual credential subscriptions. +*/ +#define KMSG_CRED_REFRESH 2 + +/*! \brief Change the password + + This message notifies credentials providers that a password change + request has been received. + + Message parameters: + - \b vparam : pointer to a ::khui_new_creds structure + */ +#define KMSG_CRED_PASSWORD 16 + +/*! \brief Initiate the process of obtaining new credentials + + The UI sends this message to start the process of obtaining new + credentials. See \ref cred_acq for more information about handling this + message. + + Message parameters: + - \b vparam : pointer to a ::khui_new_creds structure + + \see \ref cred_acq + */ +#define KMSG_CRED_NEW_CREDS 17 + +/*! \brief Renew credentials + + This is a notification sent to individual credentials providers + that a specified identity's credentials should be renewed. + + Message parameters: + - \b vparam : Pointer to a khui_new_creds object + */ +#define KMSG_CRED_RENEW_CREDS 18 + +/*! \brief Dialog setup + + Once KMSG_CRED_NEW_CREDS has been responded to by all the + credential types, the UI creates the dialog windows using the data + supplied in the ::khui_new_creds_by_type structures and issues + this message. Each credentials provider is expected to respond by + finalizing dialog creation operations. + + Message parameters: + - \b vparam : poitner to a ::khui_new_creds structure + + \note May be sent to individual credential subscriptions. + */ +#define KMSG_CRED_DIALOG_SETUP 19 + +/*! \brief Dialog pre-start + + Sent after all the credentials providers have responded to + KMSG_CRED_DIALOG_SETUP and all the initialization has been + completed. Credentials providers are expected to respond to this + message by loading any default data into the dialog controls for + each credential type. + + Message parameters: + - \b vparam : pointer to a ::khui_new_creds structure + + \note May be sent to individual credential subscriptions. + */ +#define KMSG_CRED_DIALOG_PRESTART 20 + +/*! \brief Dialog start + + A notification that the dialog is now in progress. + + Message parameters: + - \b vparam : pointer to a ::khui_new_creds structure + + \note May be sent to individual credential subscriptions. + */ +#define KMSG_CRED_DIALOG_START 21 + +/*! \brief The primary identity of the new credentials dialog has changed + + This message is not sent out by the UI, but is reserved here for + use by individual credentials providers. The message may be sent + from the dialog procedure to the plugin. + + Message parameters: + - \b vparam : pointer to a ::khui_new_creds structure + + \note Be careful when sending this message. All messages that are + not sent by the system should not be sent via broadcast. + Instead, create a subscription using kmq_create_subscription() + for the individual plugin that you want to send the message + and use one of the per-subscription message functions to send + the actual message. + */ +#define KMSG_CRED_DIALOG_NEW_IDENTITY 22 + +/*! \brief New credentials options have changed. + + This message is not sent out by the UI, but is reserved here for + use by individual credentials providers. The message may be sent + from the dialog procedure to the plugin. + + Message parameters: + - \b vparam : pointer to a ::khui_new_creds structure + + \note Be careful when sending this message. All messages that are + not sent by the system should not be sent via broadcast. + Instead, create a subscription using kmq_create_subscription() + for the individual plugin that you want to send the message + and use one of the per-subscription message functions to send + the actual message. + */ +#define KMSG_CRED_DIALOG_NEW_OPTIONS 23 + +/*! \brief Process dialog + + Sent to all the credential providers to look at the contents of + the given ::khui_new_creds structure and do any required + processing. + + If the \a result field in the structure is set to + KHUI_NC_RESULT_GET_CREDS, then new credentials should be obtained + using the given data. + + Set the \a response field in the structure to indicate how the UI + should proceed from here. + + Message parameters: + - \b vparam : pointer to a ::khui_new_creds structure + + \note May be sent to individual credential subscriptions. + */ +#define KMSG_CRED_PROCESS 24 + +/*! \brief End a credentials acquisition operation + + A notification that the credentials acquisition operation has + ended. + + Message parameters: + - \b vparam : pointer to a ::khui_new_creds structure + + \note May be sent to individual credential subscriptions. + */ +#define KMSG_CRED_END 25 + +/*! \brief Import credentials from the operating system + + Notification to all credentials providers to import any available + credentials from the operating system. + + Message parameters: + - This message does not have any parameters +*/ +#define KMSG_CRED_IMPORT 26 + +/*! \brief Destroy credentials + + Notification that the specified credentials should be destroyed. + Once this message has completed processing a ::KMSG_CRED_REFRESH + message will be issued. + + The credentials that should be destroyed are specified by a + ::khui_action_context structure. The context that should be used + is the selection context. Hence, the credentials that must be + destroyed are the ones lised in the credential set (\a credset). + + Message parameters: + + - \b upram : Unused. Zero. + + - \b vparam : pointer to a ::khui_action_context structure which + describes which credentials need to be destroyed. + + */ +#define KMSG_CRED_DESTROY_CREDS 32 + +#if 0 +/*! \brief Parse an identity + + \note May be sent to individual credential subscriptions. + */ +#define KMSG_CRED_IDENT_PARSE 65 +#endif + +/*! \brief A property page is being launced + + Message parameters: + - \b vparam : pointer to a ::khui_property_sheet structure + */ +#define KMSG_CRED_PP_BEGIN 128 + +/*! \brief A property page is about to be created + + Message parameters: + - \b vparam : pointer to a ::khui_property_sheet structure + + \note This message is merely a notification that the property + sheet is being created. Handlers should not modify the state + of the property sheet or pages at this time. + */ +#define KMSG_CRED_PP_PRECREATE 129 + +/*! \brief A property page has finished processing + + Message parameters: + - \b vparam : pointer to a ::khui_property_sheet structure + */ +#define KMSG_CRED_PP_END 130 + +/*! \brief A property page has been destroyed + + Message parameters: + - \b vparam : pointer to a ::khui_property_sheet structure + */ +#define KMSG_CRED_PP_DESTROY 131 + +/*! \brief Check if a KMSG_CRED subtype is a credentials acquisition message + + Dialog messages are those that deal with the new or initial + credentials acquisition dialog, from initial announcement to + dialog completion. + + Currently, the dialog messages are: + - ::KMSG_CRED_INITIAL_CREDS + - ::KMSG_CRED_NEW_CREDS + - ::KMSG_CRED_RENEW_CREDS + - ::KMSG_CRED_DIALOG_SETUP + - ::KMSG_CRED_DIALOG_PRESTART + - ::KMSG_CRED_DIALOG_START + - ::KMSG_CRED_DIALOG_NEW_IDENTITY + - ::KMSG_CRED_DIALOG_NEW_OPTIONS + - ::KMSG_CRED_PROCESS + - ::KMSG_CRED_END + + All dialog message numbers are allocated in a contigous block. + + Note that while ::KMSG_CRED_PROCESS and ::KMSG_CRED_END are not + specific to dialogs, they are still included in this predicate + because they are also part of the dialog message sequence. + */ +#define IS_CRED_ACQ_MSG(msg) ((msg) >= 16 && (msg) <=31) + +/*@}*/ /* /KMSG_CRED subtypes */ + +/*! \defgroup kmq_msg_alert KMSG_ALERT Subtypes + @{*/ + +/*! \brief Show an alert + + Message parameters: + - \b vparam : held pointer to a ::khui_alert object + + \note The ::khui_alert object will be released when the processing + of this message completes. + */ +#define KMSG_ALERT_SHOW 1 + +/*@}*/ + +/*! \defgroup kmq_msg_ident KMSG_IDENT Subtypes + @{*/ + +/*! \brief Initialize and start the identity provider + + + Sent by the KCDB to notify the identity provider that it is now + the current identity provider. + + Note that unlike regular plugins, an identity provider can be + loaded and inert (not provide any services). Also, the user may + switch between multiple identity providers on the fly. + */ +#define KMSG_IDENT_INIT 1 + +/*! \brief Stop the identity provider + + Sent by the KCDB as notificaton that the identity provider is no + longer the current provider. + */ +#define KMSG_IDENT_EXIT 2 + +/*! \brief Check if an identity name is valid + + This message is sent to the identity provider to verify the syntax + of an identity name. Note that only the syntax of the name is to + be verfied and not the actual physical existence of said identity. + + Message parameters: + + - \b vparam : pointer to ::kcdb_ident_name_xfer object. The + name to be validated will be in the \a name_src member. The + buffer will be NULL terminated with a maximum limit of + KCDB_IDENT_MAXCCH_NAME characters including the terminating + NULL, consisting only of characters in KCDB_IDENT_VALID_CHARS + The \a result member should be set to one of the following + depending on the result of the validation: + + - KHM_ERROR_SUCCESS : The name was valid + - KHM_ERROR_INVALID_NAME : The name was invalid + */ +#define KMSG_IDENT_VALIDATE_NAME 3 + +/*! \brief Check if an identity is valid + + Sent to the identity provider to verify the validity of the given + identity. The provider should verify that the identity exists and + is in a state where it can be actively used. + + Depending on the result of the validation, the flags of the + identity should be updated. + + Message parameters: + - \b vparam : Handle to an identity cast as a void pointer. + */ +#define KMSG_IDENT_VALIDATE_IDENTITY 4 + +/*! \brief Canonicalize identity name + + The identity provider will be given a name, which it should put in + canonical form, adjusting case and any character replacement or + doing any relevant expansions if applicable, and place it in the + supplied buffer. + + Message parameters: + + - \b vparam : Pointer to a ::kcdb_ident_name_xfer structure + which provides the identity name to canonicalize in the \a + name_src member, and the buffer to store the canonical name + in the \a name_dest member. The \a name_dest buffer is + guaranteed to be at least KCDB_IDENT_MAXCCH_NAME characters + in size. + + If the name cannot be canonicalized for some reason, the + destination buffer should be set to a zero-length string and the + \a result member of the ::kcdb_ident_name_xfer structure should be + set to the error code. If the destination buffer is set to a + zero-length string and \a result is KHM_ERROR_SUCCESS, then the + original name provided in \a name_src is assumed to be already in + canonical form. + */ +#define KMSG_IDENT_CANON_NAME 5 + +/*! \brief Compare names + + Compare two identity names. The names that are given aren't + guaranteed to be in canonical form. The return value should be + akin to strcmp(). + + Message parameters: + + - \b vparam : A pointer to a ::kcdb_ident_name_xfer structure. + The \a name_src member points at the first name, and the \a + name_alt member specifies the second name. The result of the + comparison should be place in \a result. + */ +#define KMSG_IDENT_COMPARE_NAME 6 + +/*! \brief Set the default identity + + Set or unset the default identity. To set the default identity, + the \a uparam parameter will be set to a non-zero value and a + handle to the identity will be specified in \a vparam. To unset + the default identity (i.e. not have a default identity), a zero + value will be specified in \a uparam and no identities will be + specified in \a vparam. + + When setting a default identity, the identity provider will + receive this message prior to the ::KCDB_IDENT_FLAG_DEFAULT bit + being set or reset on any identity. It should return + KHM_ERROR_SUCCESS if the requested operation can be performed. + Returning any other value will abort the operation and will leave + the default identity unchanged. + + When resetting the default identity, this message should be + treated only as a notification. + + Message parameters: + + - \a uparam : Is non-zero if an identity is being made default. If + this is zero, then identity should be the default. + + - \a vparam : A handle to the identity to be made default if \a + uparam is non-zero. NULL otherwise. + + Return value: + + - KHM_ERROR_SUCCESS : The identity should be marked as default + - Any other value : The identity should not be marked as default + + */ +#define KMSG_IDENT_SET_DEFAULT 7 + +/*! \brief Set an identity as searchable + + Set or reset the searchable bit on an identity. If the \a uparam + parameter is non-zero, then the searchable bit is being set. + Otherwise it is being reset. The identity provider should return + KHM_ERROR_SUCCESS in order to indicate that the identity should be + marked as searchable. Any other value will result in the + searchable bit being reset on the identity. + + Message parameters: + + - \a uparam : Is non-zero if the searchable bit is being set. Zero + otherwise. + + - \a vparam : Handle to the identity + + Return value: + + - KHM_ERROR_SUCCESS: The identity should be marked as searchable + - Any other value : The identity should not be marked as default + */ +#define KMSG_IDENT_SET_SEARCHABLE 8 + +/*! \brief Get information about an identity + + */ +#define KMSG_IDENT_GET_INFO 9 + +/*! \brief Enumerate known and accessible identities + */ +#define KMSG_IDENT_ENUM_KNOWN 10 + +/*! \brief Update information about an identity + */ +#define KMSG_IDENT_UPDATE 11 + +/*! \brief Retrieve the user interface callback function + + When obtaining new credentials, the user interface needs to obtain + a callback function which will provide identity selection + controls. + + Message parameters: + + - \a uparam : Not used + + - \a vparam : pointer to a ::khui_ident_new_creds_cb which will + receive the call back. + */ +#define KMSG_IDENT_GET_UI_CALLBACK 12 + +/*! \brief Notification of the creation of an identity + + This should be considered just a notification. The identit + provider does not have an opportunity to veto the creation of an + identity whose name has been found to be valid. However, when + handing this notification, the identity provider can: + + - Change the flags of the identity and/or marking the identity as + invalid. + + - Change the default identity. + + Note that this notification is sent before the general :;KMSG_KCDB + notification of the identity creation is sent. + + Message parameters: + + - \a uparam : Not used. + + - \p vparam : handle to the identity + */ +#define KMSG_IDENT_NOTIFY_CREATE 13 + +/*@}*/ /* /KMSG_IDENT subtypes */ + +/*@}*/ /* / message types */ +/*@}*/ /* / kmq */ + +#endif diff --git a/src/windows/identity/include/khthread.h b/src/windows/identity/include/khthread.h new file mode 100644 index 000000000..b7354c468 --- /dev/null +++ b/src/windows/identity/include/khthread.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/* Not exported */ +#ifndef __KHIMAIRA_KTHREAD_H +#define __KHIMAIRA_KTHREAD_H + +#ifdef _WIN32 +#define khm_mutex CRITICAL_SECTION + +#define khp_mutex_init(pcs) InitializeCriticalSection(pcs) +#define khp_mutex_destroy(pcs) DeleteCriticalSection(pcs) +#define khp_mutex_lock(pcs) EnterCriticalSection(pcs) +#define khp_mutex_unlock(pcs) LeaveCriticalSection(pcs) +#define khp_mutex_trylock(pcs) (!TryEnterCriticalSection(pcs)) + +#endif + +#endif \ No newline at end of file diff --git a/src/windows/identity/kconfig/Makefile b/src/windows/identity/kconfig/Makefile new file mode 100644 index 000000000..26619c011 --- /dev/null +++ b/src/windows/identity/kconfig/Makefile @@ -0,0 +1,51 @@ +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +MODULE=kconfig +!include <../config/Makefile.w32> + +INCFILES= \ + $(INCDIR)\kconfig.h + +OBJFILES= \ + $(OBJ)\kconfigmain.obj \ + $(OBJ)\api.obj + +all: mkdirs $(INCFILES) $(OBJFILES) + +clean:: + $(RM) $(INCFILES) + +# Tests + +test:: util_test + +util_test: $(OBJ)\utiltest.exe + $(OBJ)\utiltest.exe + +$(OBJ)\utiltest.exe: $(OBJ)\utiltest.obj + $(EXECONLINK) $(OBJFILES) + +$(OBJ)\utiltest.obj: test\utiltest.c + $(C2OBJ) diff --git a/src/windows/identity/kconfig/api.c b/src/windows/identity/kconfig/api.c new file mode 100644 index 000000000..3d69c998e --- /dev/null +++ b/src/windows/identity/kconfig/api.c @@ -0,0 +1,2098 @@ +/* +* Copyright (c) 2004 Massachusetts Institute of Technology +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, +* modify, merge, publish, distribute, sublicense, and/or sell copies +* of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +/* $Id$ */ + +#include +#include + +kconf_conf_space * conf_root = NULL; +kconf_handle * conf_handles = NULL; +kconf_handle * conf_free_handles = NULL; + +CRITICAL_SECTION cs_conf_global; +CRITICAL_SECTION cs_conf_handle; +LONG conf_init = 0; +LONG conf_status = 0; + +void init_kconf(void) { + if(InterlockedIncrement(&conf_init) == 1L) { + /* we are the first */ + InitializeCriticalSection(&cs_conf_global); + EnterCriticalSection(&cs_conf_global); + conf_root = khc_create_empty_space(); + conf_root->name = wcsdup(L"Root"); + conf_root->regpath = wcsdup(CONFIG_REGPATHW); + conf_root->refcount++; + conf_status = 1; + InitializeCriticalSection(&cs_conf_handle); + LeaveCriticalSection(&cs_conf_global); + } + /* else assume we are already initialized */ +} + +void exit_kconf(void) { + if(khc_is_config_running()) { + kconf_handle * h; + + EnterCriticalSection(&cs_conf_global); + + conf_init = 0; + conf_status = 0; + + khc_free_space(conf_root); + + EnterCriticalSection(&cs_conf_handle); + while(conf_free_handles) { + LPOP(&conf_free_handles, &h); + if(h) { + free(h); + } + } + + while(conf_handles) { + LPOP(&conf_handles, &h); + if(h) { + free(h); + } + } + LeaveCriticalSection(&cs_conf_handle); + DeleteCriticalSection(&cs_conf_handle); + + LeaveCriticalSection(&cs_conf_global); + DeleteCriticalSection(&cs_conf_global); + } +} + +kconf_handle * khc_handle_from_space(kconf_conf_space * s, khm_int32 flags) +{ + kconf_handle * h; + + EnterCriticalSection(&cs_conf_handle); + LPOP(&conf_free_handles, &h); + if(!h) { + h = malloc(sizeof(kconf_handle)); + assert(h != NULL); + } + ZeroMemory((void *) h, sizeof(kconf_handle)); + + h->magic = KCONF_HANDLE_MAGIC; + khc_space_hold(s); + h->space = s; + h->flags = flags; + + LPUSH(&conf_handles, h); + LeaveCriticalSection(&cs_conf_handle); + + return h; +} + +/* must be called with cs_conf_global held */ +void khc_handle_free(kconf_handle * h) +{ + kconf_handle * lower; + + EnterCriticalSection(&cs_conf_handle); +#ifdef DEBUG + /* check if the handle is actually in use */ + { + kconf_handle * a; + a = conf_handles; + while(a) { + if(h == a) + break; + a = LNEXT(a); + } + + if(a == NULL) { + DebugBreak(); + } + } +#endif + while(h) { + LDELETE(&conf_handles, h); + if(h->space) { + khc_space_release(h->space); + h->space = NULL; + } + lower = h->lower; + LPUSH(&conf_free_handles, h); + h = lower; + } + LeaveCriticalSection(&cs_conf_handle); +} + +kconf_handle * khc_handle_dup(kconf_handle * o) +{ + kconf_handle * h; + kconf_handle * r; + + r = khc_handle_from_space(o->space, o->flags); + h = r; + + while(o->lower) { + h->lower = khc_handle_from_space(o->lower->space, o->lower->flags); + + o = o->lower; + h = h->lower; + } + + return r; +} + +void khc_space_hold(kconf_conf_space * s) { + InterlockedIncrement(&(s->refcount)); +} + +void khc_space_release(kconf_conf_space * s) { + LONG l = InterlockedDecrement(&(s->refcount)); + if(!l) { + EnterCriticalSection(&cs_conf_global); + + if(s->regkey_machine) + RegCloseKey(s->regkey_machine); + if(s->regkey_user) + RegCloseKey(s->regkey_user); + s->regkey_machine = NULL; + s->regkey_user = NULL; + + LeaveCriticalSection(&cs_conf_global); + } +} + +/* case sensitive replacement for RegOpenKeyEx */ +LONG +khcint_RegOpenKeyEx(HKEY hkey, LPCWSTR sSubKey, DWORD ulOptions, + REGSAM samDesired, PHKEY phkResult) { + int i; + wchar_t sk_name[KCONF_MAXCCH_NAME]; + FILETIME ft; + size_t cch; + HKEY hkp; + const wchar_t * t; + LONG rv = ERROR_SUCCESS; + + hkp = hkey; + + /* descend down the components of the subkey */ + t = sSubKey; + while(TRUE) { + wchar_t * slash; + HKEY hkt; + + slash = wcschr(t, L'\\'); + if (slash == NULL) + break; + + if (FAILED(StringCchCopyN(sk_name, ARRAYLENGTH(sk_name), + t, slash - t))) { + rv = ERROR_CANTOPEN; + goto _cleanup; + } + + sk_name[slash - t] = L'\0'; + t = slash+1; + + if (khcint_RegOpenKeyEx(hkp, sk_name, ulOptions, samDesired, &hkt) == + ERROR_SUCCESS) { + + if (hkp != hkey) + RegCloseKey(hkp); + hkp = hkt; + + } else { + + rv = ERROR_CANTOPEN; + goto _cleanup; + + } + } + + /* by now hkp is a handle to the parent of the last component in + the subkey. t is a pointer to the last component. */ + + if (FAILED(StringCchLength(t, KCONF_MAXCCH_NAME, &cch))) { + rv = ERROR_CANTOPEN; + goto _cleanup; + } + + /* go through and find the case sensitive match for the key */ + + for (i=0; ;i++) { + LONG l; + DWORD dw; + + dw = ARRAYLENGTH(sk_name); + l = RegEnumKeyEx(hkp, i, sk_name, &dw, + NULL, NULL, NULL, &ft); + + if (l != ERROR_SUCCESS) { + rv = ERROR_CANTOPEN; + goto _cleanup; + } + + if (!(wcsncmp(sk_name, t, cch))) { + /* bingo! ?? */ + if (cch < KCONF_MAXCCH_NAME && + (sk_name[cch] == L'\0' || + sk_name[cch] == L'~')) { + rv = RegOpenKeyEx(hkp, sk_name, ulOptions, + samDesired, phkResult); + goto _cleanup; + } + } + } + + _cleanup: + if (hkp != hkey && hkp != NULL) + RegCloseKey(hkp); + + return rv; +} + +LONG +khcint_RegCreateKeyEx(HKEY hKey, + LPCTSTR lpSubKey, + DWORD Reserved, + LPTSTR lpClass, + DWORD dwOptions, + REGSAM samDesired, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + PHKEY phkResult, + LPDWORD lpdwDisposition) { + LONG l; + int i; + long index = 0; + wchar_t sk_name[KCONF_MAXCCH_NAME]; /* hard limit in Windows */ + FILETIME ft; + size_t cch; + const wchar_t * t; + LONG rv = ERROR_SUCCESS; + HKEY hkp = NULL; + + hkp = hKey; + t = lpSubKey; + while(TRUE) { + wchar_t * slash; + HKEY hkt; + + slash = wcschr(t, L'\\'); + if (slash == NULL) + break; + + if (FAILED(StringCchCopyN(sk_name, ARRAYLENGTH(sk_name), + t, slash - t))) { + rv = ERROR_CANTOPEN; + goto _cleanup; + } + + sk_name[slash - t] = L'\0'; + t = slash+1; + + if (khcint_RegOpenKeyEx(hkp, sk_name, 0, samDesired, &hkt) == + ERROR_SUCCESS) { + + if (hkp != hKey) + RegCloseKey(hkp); + hkp = hkt; + + } else { + + rv = RegCreateKeyEx(hKey, + lpSubKey, + Reserved, + lpClass, + dwOptions, + samDesired, + lpSecurityAttributes, + phkResult, + lpdwDisposition); + goto _cleanup; + + } + } + + if (FAILED(StringCchLength(t, KCONF_MAXCCH_NAME, &cch))) { + rv = ERROR_CANTOPEN; + goto _cleanup; + } + + for (i=0; ;i++) { + DWORD dw; + + dw = ARRAYLENGTH(sk_name); + l = RegEnumKeyEx(hkp, i, sk_name, &dw, + NULL, NULL, NULL, &ft); + + if (l != ERROR_SUCCESS) + break; + + if (!(wcsncmp(sk_name, t, cch))) { + /* bingo! ?? */ + if (sk_name[cch] == L'\0' || + sk_name[cch] == L'~') { + l = RegOpenKeyEx(hkp, sk_name, 0, + samDesired, phkResult); + if (l == ERROR_SUCCESS && lpdwDisposition) + *lpdwDisposition = REG_OPENED_EXISTING_KEY; + rv = l; + goto _cleanup; + } + } + + if (!wcsnicmp(sk_name, t, cch) && + (sk_name[cch] == L'\0' || + sk_name[cch] == L'~')) { + long new_idx; + + if (sk_name[cch] == L'\0') + new_idx = 1; + else if (cch + 1 < KCONF_MAXCCH_NAME) + new_idx = wcstol(sk_name + (cch + 1), NULL, 10); + else + return ERROR_BUFFER_OVERFLOW; + + assert(new_idx > 0); + + if (new_idx > index) + index = new_idx; + } + } + + if (index != 0) { + if (FAILED(StringCbPrintf(sk_name, sizeof(sk_name), + L"%s~%d", t, index))) + return ERROR_BUFFER_OVERFLOW; + } else { + StringCbCopy(sk_name, sizeof(sk_name), t); + } + + rv = RegCreateKeyEx(hkp, + sk_name, + Reserved, + lpClass, + dwOptions, + samDesired, + lpSecurityAttributes, + phkResult, + lpdwDisposition); + + _cleanup: + + if (hkp != hKey && hkp != NULL) + RegCloseKey(hkp); + + return rv; +} + + +HKEY khc_space_open_key(kconf_conf_space * s, khm_int32 flags) { + HKEY hk = NULL; + int nflags = 0; + DWORD disp; + if(flags & KCONF_FLAG_MACHINE) { + if(s->regkey_machine) + return s->regkey_machine; + if((khcint_RegOpenKeyEx(HKEY_LOCAL_MACHINE, s->regpath, 0, + KEY_READ | KEY_WRITE, &hk) != + ERROR_SUCCESS) && + !(flags & KHM_PERM_WRITE)) { + + if(khcint_RegOpenKeyEx(HKEY_LOCAL_MACHINE, s->regpath, 0, + KEY_READ, &hk) == ERROR_SUCCESS) { + nflags = KHM_PERM_READ; + } + + } + if(!hk && (flags & KHM_FLAG_CREATE)) { + + khcint_RegCreateKeyEx(HKEY_LOCAL_MACHINE, + s->regpath, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_READ | KEY_WRITE, + NULL, + &hk, + &disp); + } + if(hk) { + EnterCriticalSection(&cs_conf_global); + s->regkey_machine = hk; + s->regkey_machine_flags = nflags; + LeaveCriticalSection(&cs_conf_global); + } + + return hk; + } else { + if(s->regkey_user) + return s->regkey_user; + if((khcint_RegOpenKeyEx(HKEY_CURRENT_USER, s->regpath, 0, + KEY_READ | KEY_WRITE, &hk) != + ERROR_SUCCESS) && + !(flags & KHM_PERM_WRITE)) { + if(khcint_RegOpenKeyEx(HKEY_CURRENT_USER, s->regpath, 0, + KEY_READ, &hk) == ERROR_SUCCESS) { + nflags = KHM_PERM_READ; + } + } + if(!hk && (flags & KHM_FLAG_CREATE)) { + khcint_RegCreateKeyEx(HKEY_CURRENT_USER, + s->regpath, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_READ | KEY_WRITE, + NULL, + &hk, + &disp); + } + if(hk) { + EnterCriticalSection(&cs_conf_global); + s->regkey_user = hk; + s->regkey_user_flags = nflags; + LeaveCriticalSection(&cs_conf_global); + } + + return hk; + } +} + +KHMEXP khm_int32 KHMAPI khc_shadow_space(khm_handle upper, khm_handle lower) +{ + kconf_handle * h; + + if(!khc_is_config_running()) + return KHM_ERROR_NOT_READY; + + if(!khc_is_handle(upper)) + return KHM_ERROR_INVALID_PARM; + + h = (kconf_handle *) upper; + + EnterCriticalSection(&cs_conf_handle); + if(h->lower) { + LeaveCriticalSection(&cs_conf_handle); + EnterCriticalSection(&cs_conf_global); + khc_handle_free(h->lower); + LeaveCriticalSection(&cs_conf_global); + EnterCriticalSection(&cs_conf_handle); + h->lower = NULL; + } + + if(khc_is_handle(lower)) { + kconf_handle * l; + kconf_handle * lc; + + l = (kconf_handle *) lower; + LeaveCriticalSection(&cs_conf_handle); + lc = khc_handle_dup(l); + EnterCriticalSection(&cs_conf_handle); + h->lower = lc; + } + LeaveCriticalSection(&cs_conf_handle); + + return KHM_ERROR_SUCCESS; +} + +kconf_conf_space * khc_create_empty_space(void) { + kconf_conf_space * r; + + r = malloc(sizeof(kconf_conf_space)); + assert(r != NULL); + ZeroMemory(r,sizeof(kconf_conf_space)); + + return r; +} + +void khc_free_space(kconf_conf_space * r) { + kconf_conf_space * c; + + if(!r) + return; + + LPOP(&r->children, &c); + while(c) { + khc_free_space(c); + LPOP(&r->children, &c); + } + + if(r->name) + free(r->name); + + if(r->regpath) + free(r->regpath); + + if(r->regkey_machine) + RegCloseKey(r->regkey_machine); + + if(r->regkey_user) + RegCloseKey(r->regkey_user); + + free(r); +} + +khm_int32 khcint_open_space_int(kconf_conf_space * parent, wchar_t * sname, size_t n_sname, khm_int32 flags, kconf_conf_space **result) { + kconf_conf_space * p; + kconf_conf_space * c; + HKEY pkey = NULL; + HKEY ckey = NULL; + wchar_t buf[KCONF_MAXCCH_NAME]; + + if(!parent) + p = conf_root; + else + p = parent; + + if(n_sname >= KCONF_MAXCCH_NAME || n_sname <= 0) + return KHM_ERROR_INVALID_PARM; + + /*SAFE: buf: buffer size == KCONF_MAXCCH_NAME * wchar_t > + n_sname * wchar_t */ + wcsncpy(buf, sname, n_sname); + buf[n_sname] = L'\0'; + + /* see if there is already a config space by this name. if so, + return it. Note that if the configuration space is specified in a + schema, we would find it here. */ + EnterCriticalSection(&cs_conf_global); + c = TFIRSTCHILD(p); + while(c) { + if(c->name && !wcscmp(c->name, buf)) + break; + + c = LNEXT(c); + } + LeaveCriticalSection(&cs_conf_global); + + if(c) { + khc_space_hold(c); + *result = c; + return KHM_ERROR_SUCCESS; + } + + if(!(flags & KHM_FLAG_CREATE)) { + + /* we are not creating the space, so it must exist in the form of a + registry key in HKLM or HKCU. If it existed as a schema, we + would have already retured it above. */ + if(flags & KCONF_FLAG_USER) + pkey = khc_space_open_key(p, KHM_PERM_READ | KCONF_FLAG_USER); + + if((!pkey || + (khcint_RegOpenKeyEx(pkey, buf, 0, KEY_READ, &ckey) != + ERROR_SUCCESS)) + && (flags & KCONF_FLAG_MACHINE)) { + + pkey = khc_space_open_key(p, KHM_PERM_READ | KCONF_FLAG_MACHINE); + if(!pkey || + (khcint_RegOpenKeyEx(pkey, buf, 0, KEY_READ, &ckey) != + ERROR_SUCCESS)) { + *result = NULL; + return KHM_ERROR_NOT_FOUND; + } + } + + if(ckey) { + RegCloseKey(ckey); + ckey = NULL; + } + } + + c = khc_create_empty_space(); + + /*SAFE: buf: is of known length < KCONF_MAXCCH_NAME */ + c->name = wcsdup(buf); + + /*SAFE: p->regpath: is valid since it was set using this same + function. */ + /*SAFE: buf: see above */ + c->regpath = malloc((wcslen(p->regpath) + wcslen(buf) + 2) * sizeof(wchar_t)); + + assert(c->regpath != NULL); + +#pragma warning( push ) +#pragma warning( disable: 4995 ) + /*SAFE: c->regpath: allocated above to be big enough */ + /*SAFE: p->regpath: see above */ + wcscpy(c->regpath, p->regpath); + wcscat(c->regpath, L"\\"); + /*SAFE: buf: see above */ + wcscat(c->regpath, buf); +#pragma warning( pop ) + + khc_space_hold(c); + + EnterCriticalSection(&cs_conf_global); + TADDCHILD(p,c); + LeaveCriticalSection(&cs_conf_global); + + *result = c; + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI khc_open_space(khm_handle parent, wchar_t * cspace, khm_int32 flags, khm_handle * result) { + kconf_handle * h; + kconf_conf_space * p; + kconf_conf_space * c = NULL; + size_t cbsize; + wchar_t * str; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(!khc_is_config_running()) { + return KHM_ERROR_NOT_READY; + } + + if(!result || (parent && !khc_is_handle(parent))) + return KHM_ERROR_INVALID_PARM; + + if(!parent) + p = conf_root; + else { + h = (kconf_handle *) parent; + p = khc_space_from_handle(parent); + } + + khc_space_hold(p); + + /* if none of these flags are specified, make it seem like all of + them were */ + if(!(flags & KCONF_FLAG_USER) && + !(flags & KCONF_FLAG_MACHINE) && + !(flags & KCONF_FLAG_SCHEMA)) + flags |= KCONF_FLAG_USER | KCONF_FLAG_MACHINE | KCONF_FLAG_SCHEMA; + + if(cspace == NULL) { + khc_space_release(p); + *result = (khm_handle) khc_handle_from_space(p, flags); + return KHM_ERROR_SUCCESS; + } + + if(FAILED(StringCbLength(cspace, KCONF_MAXCB_PATH, &cbsize))) { + khc_space_release(p); + *result = NULL; + return KHM_ERROR_INVALID_PARM; + } + + str = cspace; + while(TRUE) { + wchar_t * end = NULL; + + if (!(flags & KCONF_FLAG_NOPARSENAME)) { + + end = wcschr(str, L'\\'); /* safe because cspace was + validated above */ +#if 0 + if(!end) + end = wcschr(str, L'/'); /* safe because cspace was + validated above */ +#endif + } + + if(!end) { + if(flags & KCONF_FLAG_TRAILINGVALUE) { + /* we are at the value component */ + c = p; + khc_space_hold(c); + break; + } else + end = str + wcslen(str); /* safe because cspace was + validated above */ + } + + rv = khcint_open_space_int(p, str, end - str, flags, &c); + + if(KHM_SUCCEEDED(rv) && (*end == L'\\' +#if 0 + || *end == L'/' +#endif + )) { + khc_space_release(p); + p = c; + c = NULL; + str = end+1; + } + else + break; + } + + khc_space_release(p); + if(KHM_SUCCEEDED(rv)) { + *result = khc_handle_from_space(c, flags); + } else + *result = NULL; + + return rv; +} + +KHMEXP khm_int32 KHMAPI khc_close_space(khm_handle csp) { + if(!khc_is_config_running()) + return KHM_ERROR_NOT_READY; + + if(!khc_is_handle(csp)) + return KHM_ERROR_INVALID_PARM; + + khc_handle_free((kconf_handle *) csp); + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI khc_read_string(khm_handle pconf, + wchar_t * pvalue, + wchar_t * buf, + khm_size * bufsize) +{ + kconf_conf_space * c; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(!khc_is_config_running()) + return KHM_ERROR_NOT_READY; + + do { + HKEY hku = NULL; + HKEY hkm = NULL; + wchar_t * value = NULL; + int free_space = 0; + khm_handle conf = NULL; + DWORD size; + DWORD type; + LONG hr; + + int i; + + if(wcschr(pvalue, L'\\') +#if 0 + || wcschr(pvalue, L'/') +#endif + ) { + + if(KHM_FAILED(khc_open_space( + pconf, + pvalue, + KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), + &conf))) + goto _shadow; + + free_space = 1; +#if 0 + wchar_t * back, * forward; + + back = wcsrchr(pvalue, L'\\'); + forward = wcsrchr(pvalue, L'/'); + value = (back > forward)?back:forward; /* works for nulls too */ +#else + value = wcsrchr(pvalue, L'\\'); +#endif + } else { + value = pvalue; + conf = pconf; + free_space = 0; + } + + if(!khc_is_handle(conf)) + goto _shadow; + + c = khc_space_from_handle(conf); + + if(khc_is_user_handle(conf)) + hku = khc_space_open_key(c, KHM_PERM_READ); + + if(khc_is_machine_handle(conf)) + hkm = khc_space_open_key(c, KHM_PERM_READ | KCONF_FLAG_MACHINE); + + size = (DWORD) *bufsize; + if(hku) { + hr = RegQueryValueEx(hku, value, NULL, &type, (LPBYTE) buf, &size); + if(hr == ERROR_SUCCESS) { + if(type != REG_SZ) { + rv = KHM_ERROR_TYPE_MISMATCH; + goto _exit; + } + else { + *bufsize = size; + /* if buf==NULL, RegQueryValueEx will return success and just return the + required buffer size in 'size' */ + rv = (buf)? KHM_ERROR_SUCCESS: KHM_ERROR_TOO_LONG; + goto _exit; + } + } else { + if(hr == ERROR_MORE_DATA) { + *bufsize = size; + rv = KHM_ERROR_TOO_LONG; + goto _exit; + } + } + } + + size = (DWORD) *bufsize; + if(hkm) { + hr = RegQueryValueEx(hkm, value, NULL, &type, (LPBYTE) buf, &size); + if(hr == ERROR_SUCCESS) { + if(type != REG_SZ) { + rv = KHM_ERROR_TYPE_MISMATCH; + goto _exit; + } + else { + *bufsize = size; + rv = (buf)? KHM_ERROR_SUCCESS: KHM_ERROR_TOO_LONG; + goto _exit; + } + } else { + if(hr == ERROR_MORE_DATA) { + *bufsize = size; + rv = KHM_ERROR_TOO_LONG; + goto _exit; + } + } + } + + if(c->schema && khc_is_schema_handle(conf)) { + for(i=0;inSchema;i++) { + if(c->schema[i].type == KC_STRING && !wcscmp(value, c->schema[i].name)) { + /* found it */ + size_t cbsize = 0; + + if(!c->schema[i].value) { + rv = KHM_ERROR_NOT_FOUND; + goto _exit; + } + + if(FAILED(StringCbLength((wchar_t *) c->schema[i].value, KCONF_MAXCB_STRING, &cbsize))) { + rv = KHM_ERROR_NOT_FOUND; + goto _exit; + } + cbsize += sizeof(wchar_t); + + if(!buf || *bufsize < cbsize) { + *bufsize = cbsize; + rv = KHM_ERROR_TOO_LONG; + goto _exit; + } + + StringCbCopy(buf, *bufsize, (wchar_t *) c->schema[i].value); + *bufsize = cbsize; + rv = KHM_ERROR_SUCCESS; + goto _exit; + } + } + } + +_shadow: + if(free_space && conf) + khc_close_space(conf); + + if(khc_is_shadowed(pconf)) { + pconf = khc_shadow(pconf); + continue; + } else { + rv = KHM_ERROR_NOT_FOUND; + break; + } + +_exit: + if(free_space && conf) + khc_close_space(conf); + break; + + } while(TRUE); + + return rv; +} + +KHMEXP khm_int32 KHMAPI khc_read_int32(khm_handle pconf, wchar_t * pvalue, khm_int32 * buf) { + kconf_conf_space * c; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(!khc_is_config_running()) + return KHM_ERROR_NOT_READY; + + if(!buf || !pvalue) + return KHM_ERROR_INVALID_PARM; + + do { + DWORD size; + DWORD type; + LONG hr; + HKEY hku = NULL; + HKEY hkm = NULL; + + wchar_t * value = NULL; + int free_space = 0; + khm_handle conf = NULL; + + int i; + + if(wcschr(pvalue, L'\\') +#if 0 + || wcschr(pvalue, L'/') +#endif + ) { + if(KHM_FAILED(khc_open_space( + pconf, + pvalue, + KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), + &conf))) + goto _shadow; + free_space = 1; +#if 0 + wchar_t * back, * forward; + + back = wcsrchr(pvalue, L'\\'); + forward = wcsrchr(pvalue, L'/'); + value = (back > forward)?back:forward; +#else + value = wcsrchr(pvalue, L'\\'); +#endif + } else { + value = pvalue; + conf = pconf; + free_space = 0; + } + + if(!khc_is_handle(conf) || !buf) + goto _shadow; + + c = khc_space_from_handle(conf); + + if(khc_is_user_handle(conf)) + hku = khc_space_open_key(c, KHM_PERM_READ); + + if(khc_is_machine_handle(conf)) + hkm = khc_space_open_key(c, KHM_PERM_READ | KCONF_FLAG_MACHINE); + + size = sizeof(DWORD); + if(hku) { + hr = RegQueryValueEx(hku, value, NULL, &type, (LPBYTE) buf, &size); + if(hr == ERROR_SUCCESS) { + if(type != REG_DWORD) { + rv = KHM_ERROR_TYPE_MISMATCH; + goto _exit; + } + else { + rv = KHM_ERROR_SUCCESS; + goto _exit; + } + } + } + + size = sizeof(DWORD); + if(hkm) { + hr = RegQueryValueEx(hkm, value, NULL, &type, (LPBYTE) buf, &size); + if(hr == ERROR_SUCCESS) { + if(type != REG_DWORD) { + rv= KHM_ERROR_TYPE_MISMATCH; + goto _exit; + } + else { + rv= KHM_ERROR_SUCCESS; + goto _exit; + } + } + } + + if(c->schema && khc_is_schema_handle(conf)) { + for(i=0;inSchema;i++) { + if(c->schema[i].type == KC_INT32 && !wcscmp(value, c->schema[i].name)) { + *buf = (khm_int32) c->schema[i].value; + rv = KHM_ERROR_SUCCESS; + goto _exit; + } + } + } +_shadow: + if(free_space && conf) + khc_close_space(conf); + + if(khc_is_shadowed(pconf)) { + pconf = khc_shadow(pconf); + continue; + } else { + rv = KHM_ERROR_NOT_FOUND; + break; + } +_exit: + if(free_space && conf) + khc_close_space(conf); + break; + } + while(TRUE); + + return rv; +} + +KHMEXP khm_int32 KHMAPI khc_read_int64(khm_handle pconf, wchar_t * pvalue, khm_int64 * buf) { + kconf_conf_space * c; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(!khc_is_config_running()) + return KHM_ERROR_NOT_READY; + + do { + DWORD size; + DWORD type; + LONG hr; + HKEY hku = NULL; + HKEY hkm = NULL; + + wchar_t * value = NULL; + int free_space = 0; + khm_handle conf = NULL; + + int i; + + if(wcschr(pvalue, L'\\') +#if 0 + || wcschr(pvalue, L'/') +#endif + ) { + if(KHM_FAILED(khc_open_space( + pconf, + pvalue, + KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), + &conf))) + goto _shadow; + free_space = 1; +#if 0 + wchar_t * back, *forward; + + back = wcsrchr(pvalue, L'\\'); + forward = wcsrchr(pvalue, L'/'); + value = (back > forward)?back:forward; +#else + value = wcsrchr(pvalue, L'\\'); +#endif + } else { + value = pvalue; + conf = pconf; + free_space = 0; + } + + if(!khc_is_handle(conf) || !buf) + goto _shadow; + + c = khc_space_from_handle(conf); + + if(khc_is_user_handle(conf)) + hku = khc_space_open_key(c, KHM_PERM_READ); + + if(khc_is_machine_handle(conf)) + hkm = khc_space_open_key(c, KHM_PERM_READ | KCONF_FLAG_MACHINE); + + size = sizeof(khm_int64); + if(hku) { + hr = RegQueryValueEx(hku, value, NULL, &type, (LPBYTE) buf, &size); + if(hr == ERROR_SUCCESS) { + if(type != REG_QWORD) { + rv= KHM_ERROR_TYPE_MISMATCH; + goto _exit; + } + else { + rv = KHM_ERROR_SUCCESS; + goto _exit; + } + } + } + + size = sizeof(khm_int64); + if(hkm) { + hr = RegQueryValueEx(hkm, value, NULL, &type, (LPBYTE) buf, &size); + if(hr == ERROR_SUCCESS) { + if(type != REG_QWORD) { + rv = KHM_ERROR_TYPE_MISMATCH; + goto _exit; + } + else { + rv = KHM_ERROR_SUCCESS; + goto _exit; + } + } + } + + if(c->schema && khc_is_schema_handle(conf)) { + for(i=0;inSchema;i++) { + if(c->schema[i].type == KC_INT64 && !wcscmp(value, c->schema[i].name)) { + *buf = (khm_int64) c->schema[i].value; + rv = KHM_ERROR_SUCCESS; + goto _exit; + } + } + } + +_shadow: + if(free_space && conf) + khc_close_space(conf); + if(khc_is_shadowed(pconf)) { + pconf = khc_shadow(pconf); + continue; + } else { + rv = KHM_ERROR_NOT_FOUND; + break; + } + +_exit: + if(free_space && conf) + khc_close_space(conf); + break; + + } while(TRUE); + return rv; +} + +KHMEXP khm_int32 KHMAPI khc_read_binary(khm_handle pconf, wchar_t * pvalue, void * buf, khm_size * bufsize) { + kconf_conf_space * c; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(!khc_is_config_running()) + return KHM_ERROR_NOT_READY; + + do { + DWORD size; + DWORD type; + LONG hr; + HKEY hku = NULL; + HKEY hkm = NULL; + + wchar_t * value = NULL; + int free_space = 0; + khm_handle conf = NULL; + + if(wcschr(pvalue, L'\\') +#if 0 + || wcschr(pvalue, L'/') +#endif + ) { + if(KHM_FAILED(khc_open_space( + pconf, + pvalue, + KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), + &conf))) + goto _shadow; + free_space = 1; +#if 0 + wchar_t * back, *forward; + + back = wcsrchr(pvalue, L'\\'); + forward = wcsrchr(pvalue, L'/'); + value = (back > forward)?back:forward; +#else + value = wcsrchr(pvalue, L'\\'); +#endif + } else { + value = pvalue; + conf = pconf; + free_space = 0; + } + + if(!khc_is_handle(conf)) + goto _shadow; + + c = khc_space_from_handle(conf); + + if(khc_is_user_handle(conf)) + hku = khc_space_open_key(c, KHM_PERM_READ); + + if(khc_is_machine_handle(conf)) + hkm = khc_space_open_key(c, KHM_PERM_READ | KCONF_FLAG_MACHINE); + + size = (DWORD) *bufsize; + if(hku) { + hr = RegQueryValueEx(hku, value, NULL, &type, (LPBYTE) buf, &size); + if(hr == ERROR_SUCCESS) { + if(type != REG_BINARY) { + rv = KHM_ERROR_TYPE_MISMATCH; + goto _exit; + } + else { + *bufsize = size; + rv = KHM_ERROR_SUCCESS; + goto _exit; + } + } else { + if(hr == ERROR_MORE_DATA) { + *bufsize = size; + rv = KHM_ERROR_TOO_LONG; + goto _exit; + } + } + } + + size = (DWORD) *bufsize; + if(hkm) { + hr = RegQueryValueEx(hkm, value, NULL, &type, (LPBYTE) buf, &size); + if(hr == ERROR_SUCCESS) { + if(type != REG_BINARY) { + rv = KHM_ERROR_TYPE_MISMATCH; + goto _exit; + } + else { + *bufsize = size; + rv = KHM_ERROR_SUCCESS; + goto _exit; + } + } else { + if(hr == ERROR_MORE_DATA) { + *bufsize = size; + rv = KHM_ERROR_TOO_LONG; + goto _exit; + } + } + } + + /* binary values aren't supported in schema */ +_shadow: + if(free_space && conf) + khc_close_space(conf); + if(khc_is_shadowed(pconf)) { + pconf = khc_shadow(pconf); + continue; + } else { + rv = KHM_ERROR_NOT_FOUND; + break; + } + +_exit: + if(free_space && conf) + khc_close_space(conf); + break; + + }while (TRUE); + + return rv; +} + +KHMEXP khm_int32 KHMAPI khc_write_string( + khm_handle pconf, + wchar_t * pvalue, + wchar_t * buf) +{ + HKEY pk = NULL; + kconf_conf_space * c; + khm_int32 rv = KHM_ERROR_SUCCESS; + LONG hr; + size_t cbsize; + wchar_t * value = NULL; + int free_space; + khm_handle conf = NULL; + + + if(!khc_is_config_running()) + return KHM_ERROR_NOT_READY; + + if(pconf && !khc_is_machine_handle(pconf) && !khc_is_user_handle(pconf)) + return KHM_ERROR_INVALID_OPERATION; + + if(wcschr(pvalue, L'\\') +#if 0 + || wcschr(pvalue, L'/') +#endif + ) { + if(KHM_FAILED(khc_open_space( + pconf, + pvalue, + KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), + &conf))) + return KHM_ERROR_INVALID_PARM; + free_space = 1; +#if 0 + wchar_t * back, *forward; + + back = wcsrchr(pvalue, L'\\'); + forward = wcsrchr(pvalue, L'/'); + value = (back > forward)?back:forward; +#else + value = wcsrchr(pvalue, L'\\'); +#endif + } else { + value = pvalue; + conf = pconf; + free_space = 0; + } + + if(!khc_is_handle(conf) || !buf) { + rv = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + c = khc_space_from_handle(conf); + + if(FAILED(StringCbLength(buf, KCONF_MAXCB_STRING, &cbsize))) { + rv = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + cbsize += sizeof(wchar_t); + + if(khc_is_user_handle(conf)) { + pk = khc_space_open_key(c, KHM_PERM_WRITE | KHM_FLAG_CREATE); + } else { + pk = khc_space_open_key(c, KHM_PERM_WRITE | KCONF_FLAG_MACHINE | KHM_FLAG_CREATE); + } + + hr = RegSetValueEx(pk, value, 0, REG_SZ, (LPBYTE) buf, (DWORD) cbsize); + + if(hr != ERROR_SUCCESS) + rv = KHM_ERROR_INVALID_OPERATION; + +_exit: + if(free_space) + khc_close_space(conf); + return rv; +} + +KHMEXP khm_int32 KHMAPI khc_write_int32( + khm_handle pconf, + wchar_t * pvalue, + khm_int32 buf) +{ + HKEY pk = NULL; + kconf_conf_space * c; + khm_int32 rv = KHM_ERROR_SUCCESS; + LONG hr; + wchar_t * value = NULL; + int free_space; + khm_handle conf = NULL; + + + if(!khc_is_config_running()) + return KHM_ERROR_NOT_READY; + + if(pconf && !khc_is_machine_handle(pconf) && !khc_is_user_handle(pconf)) + return KHM_ERROR_INVALID_OPERATION; + + if(wcschr(pvalue, L'\\') +#if 0 + || wcschr(pvalue, L'/') +#endif + ) { + if(KHM_FAILED(khc_open_space( + pconf, + pvalue, + KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), + &conf))) + return KHM_ERROR_INVALID_PARM; + free_space = 1; +#if 0 + wchar_t * back, *forward; + + back = wcsrchr(pvalue, L'\\'); + forward = wcsrchr(pvalue, L'/'); + value = (back > forward)?back:forward; +#else + value = wcsrchr(pvalue, L'\\'); +#endif + } else { + value = pvalue; + conf = pconf; + free_space = 0; + } + + if(!khc_is_handle(conf)) + return KHM_ERROR_INVALID_PARM; + + c = khc_space_from_handle( conf); + + if(khc_is_user_handle(conf)) { + pk = khc_space_open_key(c, KHM_PERM_WRITE | KHM_FLAG_CREATE); + } else { + pk = khc_space_open_key(c, KHM_PERM_WRITE | KCONF_FLAG_MACHINE | KHM_FLAG_CREATE); + } + + hr = RegSetValueEx(pk, value, 0, REG_DWORD, (LPBYTE) &buf, sizeof(khm_int32)); + + if(hr != ERROR_SUCCESS) + rv = KHM_ERROR_INVALID_OPERATION; + + if(free_space) + khc_close_space(conf); + + return rv; +} + +KHMEXP khm_int32 KHMAPI khc_write_int64(khm_handle pconf, wchar_t * pvalue, khm_int64 buf) { + HKEY pk = NULL; + kconf_conf_space * c; + khm_int32 rv = KHM_ERROR_SUCCESS; + LONG hr; + wchar_t * value = NULL; + int free_space; + khm_handle conf = NULL; + + + if(!khc_is_config_running()) + return KHM_ERROR_NOT_READY; + + if(pconf && !khc_is_machine_handle(pconf) && !khc_is_user_handle(pconf)) + return KHM_ERROR_INVALID_OPERATION; + + if(wcschr(pvalue, L'\\') +#if 0 + || wcschr(pvalue, L'/') +#endif + ) { + if(KHM_FAILED(khc_open_space( + pconf, + pvalue, + KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), + &conf))) + return KHM_ERROR_INVALID_PARM; + free_space = 1; +#if 0 + wchar_t * back, *forward; + + back = wcsrchr(pvalue, L'\\'); + forward = wcsrchr(pvalue, L'/'); + value = (back > forward)?back:forward; +#else + value = wcsrchr(pvalue, L'\\'); +#endif + } else { + value = pvalue; + conf = pconf; + free_space = 0; + } + + if(!khc_is_handle(conf)) + return KHM_ERROR_INVALID_PARM; + + c = khc_space_from_handle( conf); + + if(khc_is_user_handle(conf)) { + pk = khc_space_open_key(c, KHM_PERM_WRITE | KHM_FLAG_CREATE); + } else { + pk = khc_space_open_key(c, KHM_PERM_WRITE | KCONF_FLAG_MACHINE | KHM_FLAG_CREATE); + } + + hr = RegSetValueEx(pk, value, 0, REG_QWORD, (LPBYTE) &buf, sizeof(khm_int64)); + + if(hr != ERROR_SUCCESS) + rv = KHM_ERROR_INVALID_OPERATION; + + if(free_space) + khc_close_space(conf); + + return rv; +} + +KHMEXP khm_int32 KHMAPI khc_write_binary(khm_handle pconf, wchar_t * pvalue, void * buf, khm_size bufsize) { + HKEY pk = NULL; + kconf_conf_space * c; + khm_int32 rv = KHM_ERROR_SUCCESS; + LONG hr; + wchar_t * value = NULL; + int free_space; + khm_handle conf = NULL; + + + if(!khc_is_config_running()) + return KHM_ERROR_NOT_READY; + + if(pconf && !khc_is_machine_handle(pconf) && !khc_is_user_handle(pconf)) + return KHM_ERROR_INVALID_OPERATION; + + if(wcschr(pvalue, L'\\') +#if 0 + || wcschr(pvalue, L'/') +#endif + ) { + if(KHM_FAILED(khc_open_space( + pconf, + pvalue, + KCONF_FLAG_TRAILINGVALUE | (pconf?khc_handle_flags(pconf):0), + &conf))) + return KHM_ERROR_INVALID_PARM; + free_space = 1; +#if 0 + wchar_t * back, *forward; + + back = wcsrchr(pvalue, L'\\'); + forward = wcsrchr(pvalue, L'/'); + value = (back > forward)?back:forward; +#else + value = wcsrchr(pvalue, L'\\'); +#endif + } else { + value = pvalue; + conf = pconf; + free_space = 0; + } + + if(!khc_is_handle(conf)) + return KHM_ERROR_INVALID_PARM; + + c = khc_space_from_handle(conf); + + if(khc_is_user_handle(conf)) { + pk = khc_space_open_key(c, KHM_PERM_WRITE | KHM_FLAG_CREATE); + } else { + pk = khc_space_open_key(c, KHM_PERM_WRITE | KCONF_FLAG_MACHINE | KHM_FLAG_CREATE); + } + + hr = RegSetValueEx(pk, value, 0, REG_BINARY, buf, (DWORD) bufsize); + + if(hr != ERROR_SUCCESS) + rv = KHM_ERROR_INVALID_OPERATION; + + if(free_space) + khc_close_space(conf); + + return rv; +} + +KHMEXP khm_int32 KHMAPI khc_get_config_space_name(khm_handle conf, wchar_t * buf, khm_size * bufsize) { + kconf_conf_space * c; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(!khc_is_config_running()) + return KHM_ERROR_NOT_READY; + + if(!khc_is_handle(conf)) + return KHM_ERROR_INVALID_PARM; + + c = khc_space_from_handle(conf); + + if(!c->name) { + if(buf && *bufsize > 0) + buf[0] = L'\0'; + else { + *bufsize = sizeof(wchar_t); + rv = KHM_ERROR_TOO_LONG; + } + } else { + size_t cbsize; + + if(FAILED(StringCbLength(c->name, KCONF_MAXCB_NAME, &cbsize))) + return KHM_ERROR_UNKNOWN; + + cbsize += sizeof(wchar_t); + + if(!buf || cbsize > *bufsize) { + *bufsize = cbsize; + rv = KHM_ERROR_TOO_LONG; + } else { + StringCbCopy(buf, *bufsize, c->name); + *bufsize = cbsize; + } + } + + return rv; +} + +KHMEXP khm_int32 KHMAPI khc_get_config_space_parent(khm_handle conf, khm_handle * parent) { + kconf_conf_space * c; + + if(!khc_is_config_running()) + return KHM_ERROR_NOT_READY; + + if(!khc_is_handle(conf)) + return KHM_ERROR_INVALID_PARM; + + c = khc_space_from_handle(conf); + + if(c == conf_root || c->parent == conf_root) + *parent = NULL; + else + *parent = khc_handle_from_space(c->parent, khc_handle_flags(conf)); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI khc_get_type(khm_handle conf, wchar_t * value) { + HKEY hkm = NULL; + HKEY hku = NULL; + kconf_conf_space * c; + khm_int32 rv; + LONG hr; + DWORD type = 0; + + if(!khc_is_config_running()) + return KC_NONE; + + if(!khc_is_handle(conf)) + return KC_NONE; + + c = (kconf_conf_space *) conf; + + if(!khc_is_machine_handle(conf)) + hku = khc_space_open_key(c, KHM_PERM_READ); + hkm = khc_space_open_key(c, KHM_PERM_READ | KCONF_FLAG_MACHINE); + + if(hku) + hr = RegQueryValueEx(hku, value, NULL, &type, NULL, NULL); + if(!hku || hr != ERROR_SUCCESS) + hr = RegQueryValueEx(hkm, value, NULL, &type, NULL, NULL); + if(((!hku && !hkm) || hr != ERROR_SUCCESS) && c->schema) { + int i; + + for(i=0; inSchema; i++) { + if(!wcscmp(c->schema[i].name, value)) { + return c->schema[i].type; + } + } + + return KC_NONE; + } + + switch(type) { + case REG_MULTI_SZ: + case REG_SZ: + rv = KC_STRING; + break; + case REG_DWORD: + rv = KC_INT32; + break; + case REG_QWORD: + rv = KC_INT64; + break; + case REG_BINARY: + rv = KC_BINARY; + break; + default: + rv = KC_NONE; + } + + return rv; +} + +KHMEXP khm_int32 KHMAPI khc_value_exists(khm_handle conf, wchar_t * value) { + HKEY hku = NULL; + HKEY hkm = NULL; + kconf_conf_space * c; + khm_int32 rv = 0; + DWORD t; + int i; + + if(!khc_is_config_running()) + return KHM_ERROR_NOT_READY; + + if(!khc_is_handle(conf)) + return KHM_ERROR_INVALID_PARM; + + c = khc_space_from_handle(conf); + + if(!khc_is_machine_handle(conf)) + hku = khc_space_open_key(c, KHM_PERM_READ); + hkm = khc_space_open_key(c, KHM_PERM_READ | KCONF_FLAG_MACHINE); + + if(hku && (RegQueryValueEx(hku, value, NULL, &t, NULL, NULL) == ERROR_SUCCESS)) + rv |= KCONF_FLAG_USER; + if(hkm && (RegQueryValueEx(hkm, value, NULL, &t, NULL, NULL) == ERROR_SUCCESS)) + rv |= KCONF_FLAG_MACHINE; + + if(c->schema) { + for(i=0; inSchema; i++) { + if(!wcscmp(c->schema[i].name, value)) { + rv |= KCONF_FLAG_SCHEMA; + break; + } + } + } + + return rv; +} + +khm_boolean khc_is_valid_name(wchar_t * name) +{ + size_t cbsize; + if(FAILED(StringCbLength(name, KCONF_MAXCB_NAME, &cbsize))) + return FALSE; + return TRUE; +} + +khm_int32 khc_validate_schema(kconf_schema * schema, + int begin, + int *end) +{ + int i; + int state = 0; + int end_found = 0; + + i=begin; + while(!end_found) { + switch(state) { + case 0: /* initial. this record should start a config space */ + if(!khc_is_valid_name(schema[i].name) || + schema[i].type != KC_SPACE) + return KHM_ERROR_INVALID_PARM; + state = 1; + break; + + case 1: /* we are inside a config space, in the values area */ + if(!khc_is_valid_name(schema[i].name)) + return KHM_ERROR_INVALID_PARM; + if(schema[i].type == KC_SPACE) { + if(KHM_FAILED(khc_validate_schema(schema, i, &i))) + return KHM_ERROR_INVALID_PARM; + state = 2; + } else if(schema[i].type == KC_ENDSPACE) { + end_found = 1; + if(end) + *end = i; + } else { + if(schema[i].type != KC_STRING && + schema[i].type != KC_INT32 && + schema[i].type != KC_INT64 && + schema[i].type != KC_BINARY) + return KHM_ERROR_INVALID_PARM; + } + break; + + case 2: /* we are inside a config space, in the subspace area */ + if(schema[i].type == KC_SPACE) { + if(KHM_FAILED(khc_validate_schema(schema, i, &i))) + return KHM_ERROR_INVALID_PARM; + } else if(schema[i].type == KC_ENDSPACE) { + end_found = 1; + if(end) + *end = i; + } else { + return KHM_ERROR_INVALID_PARM; + } + break; + + default: + /* unreachable */ + return KHM_ERROR_INVALID_PARM; + } + i++; + } + + return KHM_ERROR_SUCCESS; +} + +khm_int32 khc_load_schema_i(khm_handle parent, kconf_schema * schema, int begin, int * end) +{ + int i; + int state = 0; + int end_found = 0; + kconf_conf_space * thisconf = NULL; + khm_handle h; + + i=begin; + while(!end_found) { + switch(state) { + case 0: /* initial. this record should start a config space */ + if(KHM_FAILED(khc_open_space(parent, schema[i].name, KHM_FLAG_CREATE, &h))) + return KHM_ERROR_INVALID_PARM; + thisconf = khc_space_from_handle(h); + thisconf->schema = schema + (begin + 1); + state = 1; + break; + + case 1: /* we are inside a config space, in the values area */ + if(schema[i].type == KC_SPACE) { + thisconf->nSchema = i - (begin + 1); + if(KHM_FAILED(khc_load_schema_i(h, schema, i, &i))) + return KHM_ERROR_INVALID_PARM; + state = 2; + } else if(schema[i].type == KC_ENDSPACE) { + thisconf->nSchema = i - (begin + 1); + end_found = 1; + if(end) + *end = i; + khc_close_space(h); + } + break; + + case 2: /* we are inside a config space, in the subspace area */ + if(schema[i].type == KC_SPACE) { + if(KHM_FAILED(khc_load_schema_i(h, schema, i, &i))) + return KHM_ERROR_INVALID_PARM; + } else if(schema[i].type == KC_ENDSPACE) { + end_found = 1; + if(end) + *end = i; + khc_close_space(h); + } else { + return KHM_ERROR_INVALID_PARM; + } + break; + + default: + /* unreachable */ + return KHM_ERROR_INVALID_PARM; + } + i++; + } + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI khc_load_schema(khm_handle conf, kconf_schema * schema) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(!khc_is_config_running()) + return KHM_ERROR_NOT_READY; + + if(conf && !khc_is_handle(conf)) + return KHM_ERROR_INVALID_PARM; + + if(KHM_FAILED(khc_validate_schema(schema, 0, NULL))) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_conf_global); + rv = khc_load_schema_i(conf, schema, 0, NULL); + LeaveCriticalSection(&cs_conf_global); + + return rv; +} + +khm_int32 khc_unload_schema_i(khm_handle parent, kconf_schema * schema, int begin, int * end) +{ + int i; + int state = 0; + int end_found = 0; + kconf_conf_space * thisconf = NULL; + khm_handle h; + + i=begin; + while(!end_found) { + switch(state) { + case 0: /* initial. this record should start a config space */ + if(KHM_FAILED(khc_open_space(parent, schema[i].name, 0, &h))) + return KHM_ERROR_INVALID_PARM; + thisconf = khc_space_from_handle(h); + if(thisconf->schema == (schema + (begin + 1))) { + thisconf->schema = NULL; + thisconf->nSchema = 0; + } + state = 1; + break; + + case 1: /* we are inside a config space, in the values area */ + if(schema[i].type == KC_SPACE) { + if(KHM_FAILED(khc_unload_schema_i(h, schema, i, &i))) + return KHM_ERROR_INVALID_PARM; + state = 2; + } else if(schema[i].type == KC_ENDSPACE) { + end_found = 1; + if(end) + *end = i; + khc_close_space(h); + } + break; + + case 2: /* we are inside a config space, in the subspace area */ + if(schema[i].type == KC_SPACE) { + if(KHM_FAILED(khc_unload_schema_i(h, schema, i, &i))) + return KHM_ERROR_INVALID_PARM; + } else if(schema[i].type == KC_ENDSPACE) { + end_found = 1; + if(end) + *end = i; + khc_close_space(h); + } else { + return KHM_ERROR_INVALID_PARM; + } + break; + + default: + /* unreachable */ + return KHM_ERROR_INVALID_PARM; + } + i++; + } + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI khc_unload_schema(khm_handle conf, kconf_schema * schema) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(!khc_is_config_running()) + return KHM_ERROR_NOT_READY; + + if(conf && !khc_is_handle(conf)) + return KHM_ERROR_INVALID_PARM; + + if(KHM_FAILED(khc_validate_schema(schema, 0, NULL))) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_conf_global); + rv = khc_unload_schema_i(conf, schema, 0, NULL); + LeaveCriticalSection(&cs_conf_global); + + return rv; +} + +KHMEXP khm_int32 KHMAPI khc_enum_subspaces( + khm_handle conf, + khm_handle prev, + khm_handle * next) +{ + kconf_conf_space * s; + kconf_conf_space * c; + kconf_conf_space * p; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(!khc_is_config_running()) + return KHM_ERROR_NOT_READY; + + if(!khc_is_handle(conf) || next == NULL || + (prev != NULL && !khc_is_handle(prev))) + return KHM_ERROR_INVALID_PARM; + + s = khc_space_from_handle(conf); + + if(prev == NULL) { + /* first off, we enumerate all the registry spaces regardless of + whether the handle is applicable for some registry space or not. + See notes for khc_begin_enum_subspaces() for reasons as to why + this is done (notes are in kconfig.h)*/ + + /* go through the user hive first */ + { + HKEY hk_conf; + + hk_conf = khc_space_open_key(s, 0); + if(hk_conf) { + wchar_t name[KCONF_MAXCCH_NAME]; + khm_handle h; + int idx; + + idx = 0; + while(RegEnumKey(hk_conf, idx, + name, ARRAYLENGTH(name)) == ERROR_SUCCESS) { + wchar_t * tilde; + tilde = wcschr(name, L'~'); + if (tilde) + *tilde = 0; + if(KHM_SUCCEEDED(khc_open_space(conf, name, 0, &h))) + khc_close_space(h); + idx++; + } + } + } + + /* go through the machine hive next */ + { + HKEY hk_conf; + + hk_conf = khc_space_open_key(s, KCONF_FLAG_MACHINE); + if(hk_conf) { + wchar_t name[KCONF_MAXCCH_NAME]; + khm_handle h; + int idx; + + idx = 0; + while(RegEnumKey(hk_conf, idx, + name, ARRAYLENGTH(name)) == ERROR_SUCCESS) { + wchar_t * tilde; + tilde = wcschr(name, L'~'); + if (tilde) + *tilde = 0; + + if(KHM_SUCCEEDED(khc_open_space(conf, name, + KCONF_FLAG_MACHINE, &h))) + khc_close_space(h); + idx++; + } + } + } + + /* don't need to go through schema, because that was already + done when the schema was loaded. */ + } + + /* at last we are now ready to return the results */ + EnterCriticalSection(&cs_conf_global); + if(prev == NULL) { + c = TFIRSTCHILD(s); + rv = KHM_ERROR_SUCCESS; + } else { + p = khc_space_from_handle(prev); + if(TPARENT(p) == s) + c = LNEXT(p); + else + c = NULL; + } + LeaveCriticalSection(&cs_conf_global); + + if(prev != NULL) + khc_close_space(prev); + + if(c) { + *next = khc_handle_from_space(c, khc_handle_flags(conf)); + rv = KHM_ERROR_SUCCESS; + } else { + *next = NULL; + rv = KHM_ERROR_NOT_FOUND; + } + + return rv; +} + +KHMEXP khm_int32 KHMAPI khc_write_multi_string(khm_handle conf, wchar_t * value, wchar_t * buf) +{ + size_t cb; + wchar_t *tb; + khm_int32 rv; + + if(!khc_is_config_running()) + return KHM_ERROR_NOT_READY; + if(!khc_is_handle(conf) || buf == NULL || value == NULL) + return KHM_ERROR_INVALID_PARM; + + if(multi_string_to_csv(NULL, &cb, buf) != KHM_ERROR_TOO_LONG) + return KHM_ERROR_INVALID_PARM; + + tb = malloc(cb); + assert(tb != NULL); + multi_string_to_csv(tb, &cb, buf); + rv = khc_write_string(conf, value, tb); + + free(tb); + return rv; +} + +KHMEXP khm_int32 KHMAPI khc_read_multi_string(khm_handle conf, wchar_t * value, wchar_t * buf, khm_size * bufsize) +{ + wchar_t * tb; + khm_size cbbuf; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(!khc_is_config_running()) + return KHM_ERROR_NOT_READY; + + if(!bufsize) + return KHM_ERROR_INVALID_PARM; + + rv = khc_read_string(conf, value, NULL, &cbbuf); + if(rv != KHM_ERROR_TOO_LONG) + return rv; + + tb = malloc(cbbuf); + assert(tb != NULL); + rv = khc_read_string(conf, value, tb, &cbbuf); + + if(KHM_FAILED(rv)) + goto _exit; + + rv = csv_to_multi_string(buf, bufsize, tb); + +_exit: + free(tb); + + return rv; +} diff --git a/src/windows/identity/kconfig/kconfig.h b/src/windows/identity/kconfig/kconfig.h new file mode 100644 index 000000000..22d923bd6 --- /dev/null +++ b/src/windows/identity/kconfig/kconfig.h @@ -0,0 +1,823 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KCONFIG_H +#define __KHIMAIRA_KCONFIG_H + +#include +#include + +/*! \defgroup kconf NetIDMgr Configuration Provider */ +/*@{*/ + +/*! \brief Configuration schema descriptor record + + The schema descriptor is a convenient way to provide a default set + of configuration options for a part of an application. It + describes the configuration spaces and the values and subspaces + contained in each space. + + \see kconf_load_schema() +*/ +typedef struct kconf_schema_t { + wchar_t * name; /*!< name of the object being described. + Optional for KC_ENDSPACE type object, + but required for everything else. + Names can be upto KCONF_MAXCCH_NAME + characters in length. */ + khm_int32 type; /*!< type of the object. Can be one of + KC_SPACE, KC_ENDSPACE, KC_INT32, + KC_INT64, KC_STRING or KC_BINARY */ + khm_ui_8 value; /*!< the value of the object. It is not + used for KC_SPACE and KC_ENDSPACE + typed objects. For a KC_STRING, this + contains a pointer to the string + value. The string should not be + longer than KCONF_MAXCCH_STRING + characters. KC_INT32 and KC_INT64 + objects store the value directly in + this field, while KC_BINARY objects do + not support defining a default value + here. */ + wchar_t * description;/*!< a friendly description of the value + or configuration space */ +} kconf_schema; + +/*! \name Configuration data types + @{*/ +/*! \brief Not a known type */ +#define KC_NONE 0 + +/*! \brief When used as ::kconf_schema \a type, defines the start of a configuration space. + + There should be a subsequent KC_ENDSPACE record in the schema + which defines the end of this configuration space. + + \a name specifies the name of the configuration space. Optionally + use \a description to provide a description.*/ +#define KC_SPACE 1 + +/*! \brief Ends a configuration space started with KC_SPACE */ +#define KC_ENDSPACE 2 + +/*! \brief A 32 bit integer + + Specifies a configuration parameter named \a name which is of this + type. Use \a description to provide an optional description of + the value. + + \a value specifies a default value for this parameter in the lower + 32 bits. +*/ +#define KC_INT32 3 + +/*! \brief A 64 bit integer + + Specifies a configuration parameter named \a name which is of this + type. Use \a description to provide an optional description of + the value. + + \a value specifies a default value for this parameter. +*/ +#define KC_INT64 4 + +/*! \brief A unicode string + + Specifies a configuration parameter named \a name which is of this + type. Use \a description to provide an optional description of + the value. + + \a value specifies a default value for this parameter which should + be a pointer to a NULL terminated unicode string of no more than + ::KCONF_MAXCCH_STRING characters. +*/ +#define KC_STRING 5 + +/*! \brief An unparsed binary stream + + Specifies a configuration parameter named \a name which is of this + type. Use \a description to provide an optional description of + the value. + + Default values are not supported for binary streams. \a value is + ignored. +*/ +#define KC_BINARY 6 +/*@}*/ + +/*! \brief This is the root configuration space */ +#define KCONF_FLAG_ROOT 0x00000001 + +/*! \brief Indicates the configuration store which stores user-specific information */ +#define KCONF_FLAG_USER 0x00000002 + +/*! \brief Indicates the configuration store which stores machine-specific information */ +#define KCONF_FLAG_MACHINE 0x00000004 + +/*! \brief Indicates the configuration store which stores the schema */ +#define KCONF_FLAG_SCHEMA 0x00000008 + +/*! \brief Indicates that the last component of the given configuration path is to be considered to be a configuration value */ +#define KCONF_FLAG_TRAILINGVALUE 0x00000020 + +/*! \brief Do not parse the configuration space name + + If set, disables the parsing of the configuration space for + subspaces. The space name is taken verbatim to be a configuration + space name. This can be used when there can be forward slashes or + backslahes in the name which are not escaped. + + By default, the configuration space name, + + \code + L"foo/bar" + \endcode + + is taken to mean the configuration space \a bar which is a + subspace of \a foo. If ::KCONF_FLAG_NOPARSENAME is set, then this + is taken to mean configuration space \a foo/bar. + */ +#define KCONF_FLAG_NOPARSENAME 0x00000040 + +/*! \brief Maximum number of allowed characters (including terminating NULL) in a name + + \note This is a hard limit in Windows, since we are mapping + configuration spaces to registry keys. +*/ +#define KCONF_MAXCCH_NAME 256 + +/*! \brief Maximum number of allowed bytes (including terminating NULL) in a name */ +#define KCONF_MAXCB_NAME (KCONF_MAXCCH_NAME * sizeof(wchar_t)) + +/*! \brief Maximum level of nesting for configuration spaces + */ +#define KCONF_MAX_DEPTH 16 + +/*! \brief Maximum number of allowed characters (including terminating NULL) in a configuration path */ +#define KCONF_MAXCCH_PATH (KCONF_MAXCCH_NAME * KCONF_MAX_DEPTH) + +/*! \brief Maximum number of allowed bytes (including terminating NULL) in a configuration path */ +#define KCONF_MAXCB_PATH (KCONF_MAXCCH_PATH * sizeof(wchar_t)) + +/*! \brief Maximum number of allowed characters (including terminating NULL) in a string */ +#define KCONF_MAXCCH_STRING KHM_MAXCCH_STRING + +/*! \brief Maximum number of allowed bytes (including terminating NULL) in a string */ +#define KCONF_MAXCB_STRING (KCONF_MAXCCH_STRING * sizeof(wchar_t)) + +/*! \brief Open a configuration space + + Opens the configuration space specified by \a cspace. By default, + the opened space includes user,machine and schema configuration + stores. However, you can specify a subset of these. + + If the configuration space does not exist and the \a flags specify + KHM_FLAG_CREATE, then the configuration space is created. The + stores that are affected by the create operation depend on \a + flags. If the \a flags only specifies ::KCONF_FLAG_MACHINE, then + the configuration space is created in the machine store. If \a + flags specifies any combination of stores including \a + ::KCONF_FLAG_USER, then the configuration space is created in the + user store. Note that ::KCONF_FLAG_SCHEMA is readonly. + + Once opened, use khc_close_space() to close the configuration + space. + + \param[in] parent The parent configuration space. The path + specified in \a cspace is relative to the parent. Set this to + NULL to indicate the root configuration space. + + \param[in] cspace The confiuration path. This can be up to + ::KCONF_MAXCCH_PATH characters in length. Use either + backslashes or forward slashes to specify hiearchy. Set this + to NULL to reopen the parent configuration space. + + \param[in] flags Flags. This can be a combination of KCONF_FLAG_* + constants and KHM_FLAG_CREATE. If none of ::KCONF_FLAG_USER, + ::KCONF_FLAG_MACHINE or ::KCONF_FLAG_SCHEMA is specified, then + it defaults to all three. + + \param[out] result Pointer to a handle which receives the handle + to the opened configuration space if the call succeeds. + + \note You can re-open a configuration space with different flags + such as ::KCONF_FLAG_MACHINE by specifying NULL for \a cspace + and settings \a flags to the required flags. + +*/ +KHMEXP khm_int32 KHMAPI khc_open_space(khm_handle parent, wchar_t * cspace, khm_int32 flags, khm_handle * result); + +/*! \brief Set the shadow space for a configuration handle + + The handle specified by \a lower becomes a shadow for the handle + specified by \a upper. Any configuration value that is queried in + \a upper that does not exist in \a upper will be queried in \a + lower. + + If \a upper already had a shadow handle, that handle will be + replaced by \a lower. The handle \a lower still needs to be + closed by a call to khc_close_space(). However, closing \a lower + will not affect \a upper which will still treat the configuration + space pointed to by \a lower to be it's shadow. + + Shadows are specific to handles and not configuration spaces. + Shadowing a configuration space using one handle does not affect + any other handles which may be obtained for the same configuration + space. + + Specify NULL for \a lower to remove any prior shadow. + */ +KHMEXP khm_int32 KHMAPI khc_shadow_space(khm_handle upper, khm_handle lower); + +/*! \brief Close a handle opened with khc_open_space() +*/ +KHMEXP khm_int32 KHMAPI khc_close_space(khm_handle conf); + +/*! \brief Read a string value from a configuration space + + The \a value parameter specifies the value to read from the + configuration space. This can be either a value name or a value + path consisting of a series nested configuration space names + followed by the value name all separated by backslashes or forward + slashes. + + For example: If \a conf is a handle to the configuration space \c + 'A/B/C', then the value name \c 'D/E/v' refers to the value named + \c 'v' in the configuration space \c 'A/B/C/D/E'. + + The specific configuration store that is used to access the value + depends on the flags that were specified in the call to + khc_open_space(). The precedence of configuration stores are as + follows: + + - If KCONF_FLAG_USER was specified, then the user configuration + space. + + - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine + configuration space. + + - Otherwise, if KCONF_FLAG_SCHEMA was specified, the the schema + store. + + Note that not specifying any of the configuration store specifiers + in the call to khc_open_space() is equivalent to specifying all + three. + + \param[in] buf Buffer to copy the string to. Specify NULL to just + retrieve the number of required bytes. + + \param[in,out] bufsize On entry, specifies the number of bytes of + space available at the location specified by \a buf. On exit + specifies the number of bytes actually copied or the size of + the required buffer if \a buf is NULL or insufficient. + + \retval KHM_ERROR_NOT_READY The configuration provider has not started + \retval KHM_ERROR_INVALID_PARM One or more of the supplied parameters are not valid + \retval KHM_ERROR_TYPE_MISMATCH The specified value is not a string + \retval KHM_ERROR_TOO_LONG \a buf was NULL or the size of the buffer was insufficient. The required size is in bufsize. + \retval KHM_ERROR_SUCCESS Success. The number of bytes copied is in bufsize. + + \see khc_open_space() +*/ +KHMEXP khm_int32 KHMAPI khc_read_string( + khm_handle conf, + wchar_t * value, + wchar_t * buf, + khm_size * bufsize); + +/*! \brief Read a multi-string value from a configuration space + + The \a value parameter specifies the value to read from the + configuration space. This can be either a value name or a value + path consisting of a series nested configuration space names + followed by the value name all separated by backslashes or forward + slashes. + + For example: If \a conf is a handle to the configuration space \c + 'A/B/C', then the value name \c 'D/E/v' refers to the value named + \c 'v' in the configuration space \c 'A/B/C/D/E'. + + The specific configuration store that is used to access the value + depends on the flags that were specified in the call to + khc_open_space(). The precedence of configuration stores are as + follows: + + - If KCONF_FLAG_USER was specified, then the user configuration + space. + + - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine + configuration space. + + - Otherwise, if KCONF_FLAG_SCHEMA was specified, the the schema + store. + + A multi-string is a pseudo data type. The value in the + configuration store should contain a CSV string. Each comma + separated value in the CSV string is considered to be a separate + value. Empty values are not allowed. The buffer pointed to by \a + buf will receive these values in the form of a series of NULL + terminated strings terminated by an empty string (or equivalently, + the last string will be terminated by a double NULL). + + Note that not specifying any of the configuration store specifiers + in the call to khc_open_space() is equivalent to specifying all + three. + + \param[in] buf Buffer to copy the multi-string to. Specify NULL + to just retrieve the number of required bytes. + + \param[in,out] bufsize On entry, specifies the number of bytes of + space available at the location specified by \a buf. On exit + specifies the number of bytes actually copied or the size of + the required buffer if \a buf is NULL or insufficient. + + \retval KHM_ERROR_NOT_READY The configuration provider has not started + \retval KHM_ERROR_INVALID_PARM One or more of the supplied parameters are not valid + \retval KHM_ERROR_TYPE_MISMATCH The specified value is not a string + \retval KHM_ERROR_TOO_LONG \a buf was NULL or the size of the buffer was insufficient. The required size is in bufsize. + \retval KHM_ERROR_SUCCESS Success. The number of bytes copied is in bufsize. + + \see khc_open_space() +*/ +KHMEXP khm_int32 KHMAPI khc_read_multi_string( + khm_handle conf, + wchar_t * value, + wchar_t * buf, + khm_size * bufsize); + +/*! \brief Read a 32 bit integer value from a configuration space + + The \a value parameter specifies the value to read from the + configuration space. This can be either a value name or a value + path consisting of a series nested configuration space names + followed by the value name all separated by backslashes or forward + slashes. + + For example: If \a conf is a handle to the configuration space \c + 'A/B/C', then the value name \c 'D/E/v' refers to the value named + \c 'v' in the configuration space \c 'A/B/C/D/E'. + + The specific configuration store that is used to access the value + depends on the flags that were specified in the call to + khc_open_space(). The precedence of configuration stores are as + follows: + + - If KCONF_FLAG_USER was specified, then the user configuration + space. + + - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine + configuration space. + + - Otherwise, if KCONF_FLAG_SCHEMA was specified, the the schema + store. + + Note that not specifying any of the configuration store specifiers + in the call to khc_open_space() is equivalent to specifying all + three. + + \param[in] conf Handle to a configuration space + \param[in] value The value to query + \param[out] buf The buffer to receive the value + + \retval KHM_ERROR_NOT_READY The configuration provider has not started. + \retval KHM_ERROR_SUCCESS Success. The value that was read was placed in \a buf + \retval KHM_ERROR_NOT_FOUND The specified value was not found + \retval KHM_ERROR_INVALID_PARM One or more parameters were invalid + \retval KHM_ERROR_TYPE_MISMATCH The specified value was found but was not of the correct type. + \see khc_open_space() +*/ +KHMEXP khm_int32 KHMAPI khc_read_int32( + khm_handle conf, + wchar_t * value, + khm_int32 * buf); + +/*! \brief Read a 64 bit integer value from a configuration space + + The \a value parameter specifies the value to read from the + configuration space. This can be either a value name or a value + path consisting of a series nested configuration space names + followed by the value name all separated by backslashes or forward + slashes. + + For example: If \a conf is a handle to the configuration space \c + 'A/B/C', then the value name \c 'D/E/v' refers to the value named + \c 'v' in the configuration space \c 'A/B/C/D/E'. + + The specific configuration store that is used to access the value + depends on the flags that were specified in the call to + khc_open_space(). The precedence of configuration stores are as + follows: + + - If KCONF_FLAG_USER was specified, then the user configuration + space. + + - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine + configuration space. + + - Otherwise, if KCONF_FLAG_SCHEMA was specified, the the schema + store. + + Note that not specifying any of the configuration store specifiers + in the call to khc_open_space() is equivalent to specifying all + three. + + \param[in] conf Handle to a configuration space + \param[in] value The value to query + \param[out] buf The buffer to receive the value + + \retval KHM_ERROR_NOT_READY The configuration provider has not started + \retval KHM_ERROR_SUCCESS Success. The value that was read was placed in \a buf + \retval KHM_ERROR_NOT_FOUND The specified value was not found + \retval KHM_ERROR_INVALID_PARM One or more parameters were invalid + \retval KHM_ERROR_TYPE_MISMATCH The specified value was found but was not the correct data type. + + \see khc_open_space() +*/ +KHMEXP khm_int32 KHMAPI khc_read_int64( + khm_handle conf, + wchar_t * value, + khm_int64 * buf); + +/*! \brief Read a binary value from a configuration space + + The \a value parameter specifies the value to read from the + configuration space. This can be either a value name or a value + path consisting of a series nested configuration space names + followed by the value name all separated by backslashes or forward + slashes. + + For example: If \a conf is a handle to the configuration space \c + 'A/B/C', then the value name \c 'D/E/v' refers to the value named + \c 'v' in the configuration space \c 'A/B/C/D/E'. + + The specific configuration store that is used to access the value + depends on the flags that were specified in the call to + khc_open_space(). The precedence of configuration stores are as + follows: + + - If KCONF_FLAG_USER was specified, then the user configuration + space. + + - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine + configuration space. + + Note that not specifying any of the configuration store specifiers + in the call to khc_open_space() is equivalent to specifying all + three. Also note that the schema store (KCONF_FLAG_SCHEMA) does + not support binary values. + + \param[in] buf Buffer to copy the string to. Specify NULL to just + retrieve the number of required bytes. + + \param[in,out] bufsize On entry, specifies the number of bytes of + space available at the location specified by \a buf. On exit + specifies the number of bytes actually copied or the size of + the required buffer if \a buf is NULL or insufficient. + + \retval KHM_ERROR_SUCCESS Success. The data was copied to \a buf. The number of bytes copied is stored in \a bufsize + \retval KHM_ERROR_NOT_FOUND The specified value was not found + \retval KHM_ERROR_INVALID_PARM One or more parameters were invalid. + + \see khc_open_space() +*/ +KHMEXP khm_int32 KHMAPI khc_read_binary( + khm_handle conf, + wchar_t * value, + void * buf, + khm_size * bufsize); + +/*! \brief Write a string value to a configuration space + + The \a value parameter specifies the value to write to the + configuration space. This can be either a value name or a value + path consisting of a series nested configuration space names + followed by the value name all separated by backslashes or forward + slashes. + + For example: If \a conf is a handle to the configuration space \c + 'A/B/C', then the value name \c 'D/E/v' refers to the value named + \c 'v' in the configuration space \c 'A/B/C/D/E'. + + The specific configuration store that is used to write the value + depends on the flags that were specified in the call to + khc_open_space(). The precedence of configuration stores are as + follows: + + - If KCONF_FLAG_USER was specified, then the user configuration + space. + + - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine + configuration space. + + Note that not specifying any of the configuration store specifiers + in the call to khc_open_space() is equivalent to specifying all + three. Also note that the schema store (KCONF_FLAG_SCHEMA) is + readonly. + + \param[in] conf Handle to a configuration space + \param[in] value Name of value to write + \param[in] buf A NULL terminated unicode string not exceeding KCONF_MAXCCH_STRING in characters including terminating NULL + + \see khc_open_space() +*/ +KHMEXP khm_int32 KHMAPI khc_write_string( + khm_handle conf, + wchar_t * value, + wchar_t * buf); + +/*! \brief Write a multi-string value to a configuration space + + The \a value parameter specifies the value to write to the + configuration space. This can be either a value name or a value + path consisting of a series nested configuration space names + followed by the value name all separated by backslashes or forward + slashes. + + For example: If \a conf is a handle to the configuration space \c + 'A/B/C', then the value name \c 'D/E/v' refers to the value named + \c 'v' in the configuration space \c 'A/B/C/D/E'. + + The specific configuration store that is used to write the value + depends on the flags that were specified in the call to + khc_open_space(). The precedence of configuration stores are as + follows: + + A multi-string is a pseudo data type. The buffer pointed to by \a + buf should contain a sequence of NULL terminated strings + terminated by an empty string (or equivalently, the last string + should terminate with a double NULL). This will be stored in the + value as a CSV string. + + - If KCONF_FLAG_USER was specified, then the user configuration + space. + + - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine + configuration space. + + Note that not specifying any of the configuration store specifiers + in the call to khc_open_space() is equivalent to specifying all + three. Also note that the schema store (KCONF_FLAG_SCHEMA) is + readonly. + + \see khc_open_space() +*/ +KHMEXP khm_int32 KHMAPI khc_write_multi_string( + khm_handle conf, + wchar_t * value, + wchar_t * buf); + +/*! \brief Write a 32 bit integer value to a configuration space + + The \a value parameter specifies the value to write to the + configuration space. This can be either a value name or a value + path consisting of a series nested configuration space names + followed by the value name all separated by backslashes or forward + slashes. + + For example: If \a conf is a handle to the configuration space \c + 'A/B/C', then the value name \c 'D/E/v' refers to the value named + \c 'v' in the configuration space \c 'A/B/C/D/E'. + + The specific configuration store that is used to write the value + depends on the flags that were specified in the call to + khc_open_space(). The precedence of configuration stores are as + follows: + + - If KCONF_FLAG_USER was specified, then the user configuration + space. + + - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine + configuration space. + + Note that not specifying any of the configuration store specifiers + in the call to khc_open_space() is equivalent to specifying all + three. Also note that the schema store (KCONF_FLAG_SCHEMA) is + readonly. + + \see khc_open_space() +*/ +KHMEXP khm_int32 KHMAPI khc_write_int32( + khm_handle conf, + wchar_t * value, + khm_int32 buf); + +/*! \brief Write a 64 bit integer value to a configuration space + + The \a value parameter specifies the value to write to the + configuration space. This can be either a value name or a value + path consisting of a series nested configuration space names + followed by the value name all separated by backslashes or forward + slashes. + + For example: If \a conf is a handle to the configuration space \c + 'A/B/C', then the value name \c 'D/E/v' refers to the value named + \c 'v' in the configuration space \c 'A/B/C/D/E'. + + The specific configuration store that is used to write the value + depends on the flags that were specified in the call to + khc_open_space(). The precedence of configuration stores are as + follows: + + - If KCONF_FLAG_USER was specified, then the user configuration + space. + + - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine + configuration space. + + Note that not specifying any of the configuration store specifiers + in the call to khc_open_space() is equivalent to specifying all + three. Also note that the schema store (KCONF_FLAG_SCHEMA) is + readonly. + + \see khc_open_space() +*/ +KHMEXP khm_int32 KHMAPI khc_write_int64( + khm_handle conf, + wchar_t * value, + khm_int64 buf); + +/*! \brief Write a binary value to a configuration space + + The \a value parameter specifies the value to write to the + configuration space. This can be either a value name or a value + path consisting of a series nested configuration space names + followed by the value name all separated by backslashes or forward + slashes. + + For example: If \a conf is a handle to the configuration space \c + 'A/B/C', then the value name \c 'D/E/v' refers to the value named + \c 'v' in the configuration space \c 'A/B/C/D/E'. + + The specific configuration store that is used to write the value + depends on the flags that were specified in the call to + khc_open_space(). The precedence of configuration stores are as + follows: + + - If KCONF_FLAG_USER was specified, then the user configuration + space. + + - Otherwise, if KCONF_FLAG_MACHINE was specified, then the machine + configuration space. + + Note that not specifying any of the configuration store specifiers + in the call to khc_open_space() is equivalent to specifying all + three. Also note that the schema store (KCONF_FLAG_SCHEMA) is + readonly. + + \see khc_open_space() +*/ +KHMEXP khm_int32 KHMAPI khc_write_binary( + khm_handle conf, + wchar_t * value, + void * buf, + khm_size bufsize); + +/*! \brief Get the type of a value in a configuration space + + \return The return value is the type of the specified value, or + KC_NONE if the value does not exist. + */ +KHMEXP khm_int32 KHMAPI khc_get_type(khm_handle conf, wchar_t * value); + +/*! \brief Check which configuration stores contain a specific value. + + Each value in a configuration space can be contained in zero or + more configuration stores. Use this function to determine which + configuration stores contain the specific value. + + The returned bitmask always indicates a subset of the + configuration stores that were specified when opening the + configuration space corresponding to \a conf. + + \return A combination of ::KCONF_FLAG_MACHINE, ::KCONF_FLAG_USER + and ::KCONF_FLAG_SCHEMA indicating which stores contain the + value. + */ +KHMEXP khm_int32 KHMAPI khc_value_exists(khm_handle conf, wchar_t * value); + +/*! \brief Get the name of a configuration space + + \param[in] conf Handle to a configuration space + + \param[out] buf The buffer to receive the name. Set to NULL if + only the size of the buffer is required. + + \param[in,out] bufsize On entry, holds the size of the buffer + pointed to by \a buf. On exit, holds the number of bytes + copied into the buffer including the NULL terminator. + */ +KHMEXP khm_int32 KHMAPI khc_get_config_space_name( + khm_handle conf, + wchar_t * buf, + khm_size * bufsize); + +/*! \brief Get a handle to the parent space + + \param[in] conf Handle to a configuration space + + \param[out] parent Handle to the parent configuration space if the + call succeeds. Receives NULL otherwise. The returned handle + must be closed using khc_close_space() + */ +KHMEXP khm_int32 KHMAPI khc_get_config_space_parent( + khm_handle conf, + khm_handle * parent); + +/*! \brief Load a configuration schema into the specified configuration space + + \param[in] conf Handle to a configuration space or NULL to use the + root configuration space. + + \param[in] schema The schema to load. The schema is assumed to be + well formed. + + \see khc_unload_schema() + */ +KHMEXP khm_int32 KHMAPI khc_load_schema( + khm_handle conf, + kconf_schema * schema); + +/*! \brief Unload a schema from a configuration space + */ +KHMEXP khm_int32 KHMAPI khc_unload_schema( + khm_handle conf, + kconf_schema * schema); + +/*! \brief Enumerate the subspaces of a configuration space + + Prepares a configuration space for enumeration and returns the + child spaces in no particular order. + + \param[in] conf The configuration space to enumerate child spaces + + \param[in] prev The previous configuration space returned by + khc_enum_subspaces() or NULL if this is the first call. If + this is not NULL, then the handle passed in \a prev will be + freed. + + \param[out] next If \a prev was NULL, receives the first sub space + found in \a conf. You must \b either call + khc_enum_subspaces() again with the returned handle or call + khc_close_space() to free the returned handle if no more + subspaces are required. \a next can point to the same handle + specified in \a prev. + + \retval KHM_ERROR_SUCCESS The call succeeded. There is a valid + handle to a configuration space in \a first_subspace. + + \retval KHM_ERROR_INVALID_PARM Either \a conf or \a prev was not a + valid configuration space handle or \a first_subspace is NULL. + Note that \a prev can be NULL. + + \retval KHM_ERROR_NOT_FOUND There were no subspaces in the + configuration space pointed to by \a conf. + + \note The configuration spaces that are enumerated directly belong + to the configuration space given by \a conf. This function + does not enumerate subspaces of shadowed configuration spaces + (see khc_shadow_space()). Even if \a conf was obtained on a + restricted domain (i.e. you specified one or more + configuration stores when you openend the handle and didn't + include all the configuration stores. See khc_open_space()), + the subspaces that are returned are the union of all + configuration spaces in all the configuration stores. This is + not a bug. This is a feature. In NetIDMgr, a configuartion + space exists if some configuration store defines it (or it was + created with a call to khc_open_space() even if no + configuration store defines it yet). This is the tradeoff you + make when using a layered configuration system. + + However, the returned handle has the same domain restrictions + as \a conf. + */ +KHMEXP khm_int32 KHMAPI khc_enum_subspaces( + khm_handle conf, + khm_handle prev, + khm_handle * next); + +/*@}*/ + +#endif diff --git a/src/windows/identity/kconfig/kconfiginternal.h b/src/windows/identity/kconfig/kconfiginternal.h new file mode 100644 index 000000000..1b4b70170 --- /dev/null +++ b/src/windows/identity/kconfig/kconfiginternal.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KCONFIGINTERNAL_H +#define __KHIMAIRA_KCONFIGINTERNAL_H + +#include +#include +#include +#include +#include +#include + +/* TODO: Implement configuration provider interfaces + +typedef struct kconf_provider_t { + +} kconf_provider; +*/ + +typedef struct kconf_conf_space_t { + wchar_t * name; + + /* kconf_provider * provider; */ + + /* the regpath is the cumulative path starting from a hive root */ + wchar_t * regpath; + HKEY regkey_user; + khm_int32 regkey_user_flags; + HKEY regkey_machine; + khm_int32 regkey_machine_flags; + + khm_int32 refcount; + khm_int32 flags; + + kconf_schema * schema; + khm_int32 nSchema; + + TDCL(struct kconf_conf_space_t); +} kconf_conf_space; + +#define KCONF_SPACE_FLAG_SCHEMA 32 + +typedef struct kconf_conf_handle_t { + khm_int32 magic; + khm_int32 flags; + kconf_conf_space * space; + + struct kconf_conf_handle_t * lower; + + LDCL(struct kconf_conf_handle_t); +} kconf_handle; + +#define KCONF_HANDLE_MAGIC 0x38eb49d2 +#define khc_is_handle(h) ((h) && ((kconf_handle *)h)->magic == KCONF_HANDLE_MAGIC) +#define khc_shadow(h) (((kconf_handle *)h)->lower) +#define khc_is_shadowed(h) (khc_is_handle(h) && khc_shadow(h) != NULL) + +extern kconf_conf_space * conf_root; +extern kconf_handle * conf_handles; +extern kconf_handle * conf_free_handles; +extern CRITICAL_SECTION cs_conf_global; +extern LONG conf_init; +extern LONG conf_status; + +#define khc_is_config_running() (conf_init && conf_status) + +#define CONFIG_REGPATHW L"Software\\MIT\\NetIDMgr" + +void init_kconf(void); +void exit_kconf(void); + +/* handle operations */ +#define khc_space_from_handle(h) (((kconf_handle *) h)->space) +#define khc_is_schema_handle(h) (((kconf_handle *) h)->flags & KCONF_FLAG_SCHEMA) +#define khc_is_user_handle(h) (((kconf_handle *) h)->flags & KCONF_FLAG_USER) +#define khc_is_machine_handle(h) (((kconf_handle *) h)->flags & KCONF_FLAG_MACHINE) +#define khc_handle_flags(h) (((kconf_handle *) h)->flags) + +kconf_handle * khc_handle_from_space(kconf_conf_space * s, khm_int32 flags); +void khc_handle_free(kconf_handle * h); + +kconf_conf_space * khc_create_empty_space(void); +void khc_free_space(kconf_conf_space * r); + +void khc_space_hold(kconf_conf_space * s); +void khc_space_release(kconf_conf_space * s); + +HKEY khc_space_open_key(kconf_conf_space * s, khm_int32 flags); + +#endif diff --git a/src/windows/identity/kconfig/kconfigmain.c b/src/windows/identity/kconfig/kconfigmain.c new file mode 100644 index 000000000..8a9d6578c --- /dev/null +++ b/src/windows/identity/kconfig/kconfigmain.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include + +void +kconfig_process_attach(void) { + init_kconf(); +} + +void +kconfig_process_detach(void) { + exit_kconf(); +} diff --git a/src/windows/identity/kconfig/registry.c b/src/windows/identity/kconfig/registry.c new file mode 100644 index 000000000..4a7b46682 --- /dev/null +++ b/src/windows/identity/kconfig/registry.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include + diff --git a/src/windows/identity/kconfig/test/utiltest.c b/src/windows/identity/kconfig/test/utiltest.c new file mode 100644 index 000000000..0652c63b9 --- /dev/null +++ b/src/windows/identity/kconfig/test/utiltest.c @@ -0,0 +1,207 @@ +#include +#include +#include + +struct string_pair { + wchar_t * ms; + wchar_t * csv; +}; + +struct string_pair strings[] = { + {L"foo\0bar\0baz,quux\0ab\"cd\0", L"foo,bar,\"baz,quux\",\"ab\"\"cd\""}, + {L"a\0b\0c\0d\0e\0", L"a,b,c,d,e"}, + {L"1\0", L"1"}, + {L"\0", L""}, + {L"b\0a\0", L"b,a"}, + {L"c\0a\0b\0", L"c,a,b"}, + {L"c\0a\0B\0", L"c,a,B"}, + {L"sdf\0Bar\0Foo\0BBB\0", L"sdf,Bar,Foo,BBB"} +}; + +int n_strings = ARRAYLENGTH(strings); + +void print_ms(wchar_t * ms) { + wchar_t * s; + size_t cch; + + s = ms; + while(*s) { + printf("%S\\0", s); + StringCchLength(s, 512, &cch); + s += cch + 1; + } +} + +int ms_to_csv_test(void) { + wchar_t wbuf[512]; + int i; + khm_int32 code = 0; + size_t cbbuf; + size_t cbr; + size_t cbnull; + + printf("khc_multi_string_to_csv() test:\n"); + + for(i=0; i"); + code = khc_multi_string_to_csv(NULL, &cbnull, strings[i].ms); + code = khc_multi_string_to_csv(wbuf, &cbbuf, strings[i].ms); + if(code) { + printf(" returned %d\n", code); + return code; + } + printf("CSV[%S]", wbuf); + if(wcscmp(wbuf, strings[i].csv)) { + printf(" MISMATCH!"); + return 1; + } + + StringCbLength(wbuf, sizeof(wbuf), &cbr); + cbr+= sizeof(wchar_t); + + if(cbr != cbbuf) { + printf(" Length mismatch"); + return 1; + } + + if(cbnull != cbr) { + printf(" NULL length mismatch"); + return 1; + } + + printf("\n"); + } + + return code; +} + +int csv_to_ms_test(void) { + wchar_t wbuf[512]; + int i; + khm_int32 code = 0; + size_t cbbuf; + size_t cbr; + size_t cbnull; + + printf("khc_csv_to_multi_string() test:\n"); + + for(i=0; i", strings[i].csv); + code = khc_csv_to_multi_string(NULL, &cbnull, strings[i].csv); + code = khc_csv_to_multi_string(wbuf, &cbbuf, strings[i].csv); + if(code) { + printf(" returned %d\n", code); + return code; + } + printf("MS["); + print_ms(wbuf); + printf("]"); + + if(cbnull != cbbuf) { + printf(" NULL length mismatch"); + return 1; + } + + printf("\n"); + + printf(" Byte length:%d\n", cbbuf); + } + + return code; +} + +int ms_append_test(void) +{ + wchar_t wbuf[512]; + size_t cbbuf; + khm_int32 code; + int i; + + printf("khc_multi_string_append() test:\n"); + + for(i=0; i + +INCFILES= \ + $(INCDIR)\kcreddb.h + +OBJFILES= \ + $(OBJ)\buf.obj \ + $(OBJ)\attrib.obj \ + $(OBJ)\credential.obj \ + $(OBJ)\credset.obj \ + $(OBJ)\credtype.obj \ + $(OBJ)\identity.obj \ + $(OBJ)\init.obj \ + $(OBJ)\kcreddbmain.obj \ + $(OBJ)\type.obj \ + $(OBJ)\kcdbconfig.obj + +$(OBJ)\kcdbconfig.c: kcdbconfig.csv $(CONFDIR)\csvschema.cfg + $(CCSV) $** $@ + +$(OBJ)\kcredres.res: lang\en_us\kcredres.rc + $(RC2RES) + +all: mkdirs $(INCFILES) $(OBJ)\kcredres.res $(OBJFILES) + +clean:: + $(RM) $(INCFILES) diff --git a/src/windows/identity/kcreddb/attrib.c b/src/windows/identity/kcreddb/attrib.c new file mode 100644 index 000000000..e43540dc5 --- /dev/null +++ b/src/windows/identity/kcreddb/attrib.c @@ -0,0 +1,853 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include + +CRITICAL_SECTION cs_attrib; +hashtable * kcdb_attrib_namemap = NULL; +kcdb_attrib_i ** kcdb_attrib_tbl = NULL; +kcdb_attrib_i ** kcdb_property_tbl = NULL; +kcdb_attrib_i * kcdb_attribs = NULL; + +void kcdb_attrib_add_ref_func(const void * key, void * va) +{ + kcdb_attrib_hold((kcdb_attrib_i *) va); +} + +void kcdb_attrib_del_ref_func(const void * key, void * va) +{ + kcdb_attrib_release((kcdb_attrib_i *) va); +} + +void kcdb_attrib_msg_completion(kmq_message * m) +{ + if(m && m->vparam) { + kcdb_attrib_release((kcdb_attrib_i *) m->vparam); + } +} + +khm_int32 kcdb_attrib_hold(kcdb_attrib_i * ai) +{ + if(!ai) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_attrib); + ai->refcount++; + LeaveCriticalSection(&cs_attrib); + return KHM_ERROR_SUCCESS; +} + +khm_int32 kcdb_attrib_release(kcdb_attrib_i * ai) +{ + if(!ai) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_attrib); + ai->refcount--; + LeaveCriticalSection(&cs_attrib); + return KHM_ERROR_SUCCESS; +} + +void kcdb_attrib_post_message(khm_int32 op, kcdb_attrib_i * ai) +{ + kcdb_attrib_hold(ai); + kmq_post_message(KMSG_KCDB, KMSG_KCDB_ATTRIB, op, (void *) ai); +} + +khm_int32 KHMAPI kcdb_attr_sys_cb(khm_handle vcred, + khm_int32 attr, + void * buf, + khm_size * pcb_buf) +{ + kcdb_cred * c; + + c = (kcdb_cred *) vcred; + + switch(attr) { + case KCDB_ATTR_NAME: + return kcdb_cred_get_name(vcred, buf, pcb_buf); + + case KCDB_ATTR_ID: + if(buf && *pcb_buf >= sizeof(khm_ui_8)) { + *pcb_buf = sizeof(khm_int64); + *((khm_ui_8 *) buf) = (khm_ui_8) c->identity; + return KHM_ERROR_SUCCESS; + } else { + *pcb_buf = sizeof(khm_ui_8); + return KHM_ERROR_TOO_LONG; + } + + case KCDB_ATTR_ID_NAME: + return kcdb_identity_get_name((khm_handle) c->identity, + (wchar_t *) buf, pcb_buf); + + case KCDB_ATTR_TYPE: + if(buf && *pcb_buf >= sizeof(khm_int32)) { + *pcb_buf = sizeof(khm_int32); + *((khm_int32 *) buf) = c->type; + return KHM_ERROR_SUCCESS; + } else { + *pcb_buf = sizeof(khm_int32); + return KHM_ERROR_TOO_LONG; + } + + case KCDB_ATTR_TYPE_NAME: + return kcdb_credtype_describe(c->type, buf, + pcb_buf, KCDB_TS_SHORT); + + case KCDB_ATTR_TIMELEFT: + { + /* we are going to make liberal use of __int64 here. It + is equivalent to FILETIME and also the MSDN docs say we + should use it if the compiler supports it */ + khm_int32 rv = KHM_ERROR_SUCCESS; + unsigned __int64 ftc; + SYSTEMTIME st; + + if(!buf || *pcb_buf < sizeof(__int64)) { + *pcb_buf = sizeof(__int64); + rv = KHM_ERROR_TOO_LONG; + } else if(!kcdb_cred_buf_exist(c,KCDB_ATTR_EXPIRE)) { + *pcb_buf = sizeof(__int64); + /* setting the timeleft to _I64_MAX has the + interpretation that this credential does not + expire, which is the default behavior if the + expiration time is not known */ + *((__int64 *) buf) = _I64_MAX; + } else { + GetSystemTime(&st); + SystemTimeToFileTime(&st, (LPFILETIME) &ftc); + *((__int64 *) buf) = + *((__int64 *) kcdb_cred_buf_get(c,KCDB_ATTR_EXPIRE)) - ftc; + } + + return rv; + } + + case KCDB_ATTR_RENEW_TIMELEFT: + { + /* we are going to make liberal use of __int64 here. It + is equivalent to FILETIME and also the MSDN docs say we + should use it if the compiler supports it */ + khm_int32 rv = KHM_ERROR_SUCCESS; + unsigned __int64 ftc; + SYSTEMTIME st; + + if(!buf || *pcb_buf < sizeof(__int64)) { + *pcb_buf = sizeof(__int64); + rv = KHM_ERROR_TOO_LONG; + } else if(!kcdb_cred_buf_exist(c,KCDB_ATTR_RENEW_EXPIRE)) { + *pcb_buf = sizeof(__int64); + /* setting the timeleft to _I64_MAX has the + interpretation that this credential does not + expire, which is the default behavior if the + expiration time is not known */ + *((__int64 *) buf) = _I64_MAX; + } else { + GetSystemTime(&st); + SystemTimeToFileTime(&st, (LPFILETIME) &ftc); + *((__int64 *) buf) = + *((__int64 *) kcdb_cred_buf_get(c,KCDB_ATTR_RENEW_EXPIRE)) - ftc; + } + + return rv; + } + + case KCDB_ATTR_FLAGS: + if(buf && *pcb_buf >= sizeof(khm_int32)) { + *pcb_buf = sizeof(khm_int32); + *((khm_int32 *) buf) = c->flags; + return KHM_ERROR_SUCCESS; + } else { + *pcb_buf = sizeof(khm_int32); + return KHM_ERROR_TOO_LONG; + } + + default: + return KHM_ERROR_NOT_FOUND; + } +} + +void kcdb_attrib_init(void) +{ + kcdb_attrib attrib; + wchar_t sbuf[256]; + + InitializeCriticalSection(&cs_attrib); + kcdb_attrib_namemap = hash_new_hashtable( + KCDB_ATTRIB_HASH_SIZE, + hash_string, + hash_string_comp, + kcdb_attrib_add_ref_func, + kcdb_attrib_del_ref_func); + + kcdb_attrib_tbl = + malloc(sizeof(kcdb_attrib_i *) * (KCDB_ATTR_MAX_ID + 1)); + assert(kcdb_attrib_tbl != NULL); + ZeroMemory(kcdb_attrib_tbl, + sizeof(kcdb_attrib_i *) * (KCDB_ATTR_MAX_ID + 1)); + + kcdb_property_tbl = + malloc(sizeof(kcdb_attrib_i *) * KCDB_ATTR_MAX_PROPS); + assert(kcdb_property_tbl != NULL); + ZeroMemory(kcdb_property_tbl, + sizeof(kcdb_attrib_i *) * KCDB_ATTR_MAX_PROPS); + + kcdb_attribs = NULL; + + /* register standard attributes */ + + /* Name */ + attrib.id = KCDB_ATTR_NAME; + attrib.name = KCDB_ATTRNAME_NAME; + attrib.type = KCDB_TYPE_STRING; + LoadString(hinst_kcreddb, IDS_NAME, sbuf, ARRAYLENGTH(sbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = NULL; + attrib.flags = + KCDB_ATTR_FLAG_REQUIRED | + KCDB_ATTR_FLAG_COMPUTED | + KCDB_ATTR_FLAG_SYSTEM; + attrib.compute_cb = kcdb_attr_sys_cb; + attrib.compute_min_cbsize = sizeof(wchar_t); + attrib.compute_max_cbsize = KCDB_MAXCB_NAME; + + kcdb_attrib_register(&attrib, NULL); + + /* ID */ + attrib.id = KCDB_ATTR_ID; + attrib.name = KCDB_ATTRNAME_ID; + attrib.type = KCDB_TYPE_INT64; + LoadString(hinst_kcreddb, IDS_IDENTITY, sbuf, ARRAYLENGTH(sbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = NULL; + attrib.flags = + KCDB_ATTR_FLAG_REQUIRED | + KCDB_ATTR_FLAG_COMPUTED | + KCDB_ATTR_FLAG_SYSTEM | + KCDB_ATTR_FLAG_HIDDEN; + attrib.compute_cb = kcdb_attr_sys_cb; + attrib.compute_min_cbsize = sizeof(khm_int32); + attrib.compute_max_cbsize = sizeof(khm_int32); + + kcdb_attrib_register(&attrib, NULL); + + /* ID Name */ + attrib.id = KCDB_ATTR_ID_NAME; + attrib.alt_id = KCDB_ATTR_ID; + attrib.name = KCDB_ATTRNAME_ID_NAME; + attrib.type = KCDB_TYPE_STRING; + LoadString(hinst_kcreddb, IDS_IDENTITY, sbuf, ARRAYLENGTH(sbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = NULL; + attrib.flags = + KCDB_ATTR_FLAG_REQUIRED | + KCDB_ATTR_FLAG_COMPUTED | + KCDB_ATTR_FLAG_ALTVIEW | + KCDB_ATTR_FLAG_SYSTEM; + attrib.compute_cb = kcdb_attr_sys_cb; + attrib.compute_min_cbsize = sizeof(wchar_t); + attrib.compute_max_cbsize = KCDB_IDENT_MAXCB_NAME; + + kcdb_attrib_register(&attrib, NULL); + + /* Type */ + attrib.id = KCDB_ATTR_TYPE; + attrib.name = KCDB_ATTRNAME_TYPE; + attrib.type = KCDB_TYPE_INT32; + LoadString(hinst_kcreddb, IDS_TYPE, sbuf, ARRAYLENGTH(sbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = NULL; + attrib.flags = + KCDB_ATTR_FLAG_REQUIRED | + KCDB_ATTR_FLAG_COMPUTED | + KCDB_ATTR_FLAG_SYSTEM | + KCDB_ATTR_FLAG_HIDDEN; + attrib.compute_cb = kcdb_attr_sys_cb; + attrib.compute_min_cbsize = sizeof(khm_int32); + attrib.compute_max_cbsize = sizeof(khm_int32); + + kcdb_attrib_register(&attrib, NULL); + + /* Type Name */ + attrib.id = KCDB_ATTR_TYPE_NAME; + attrib.alt_id = KCDB_ATTR_TYPE; + attrib.name = KCDB_ATTRNAME_TYPE_NAME; + attrib.type = KCDB_TYPE_STRING; + LoadString(hinst_kcreddb, IDS_TYPE, sbuf, ARRAYLENGTH(sbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = NULL; + attrib.flags = + KCDB_ATTR_FLAG_REQUIRED | + KCDB_ATTR_FLAG_COMPUTED | + KCDB_ATTR_FLAG_ALTVIEW | + KCDB_ATTR_FLAG_SYSTEM; + attrib.compute_cb = kcdb_attr_sys_cb; + attrib.compute_min_cbsize = sizeof(wchar_t); + attrib.compute_max_cbsize = KCDB_MAXCB_NAME; + + kcdb_attrib_register(&attrib, NULL); + + /* Parent Name */ + attrib.id = KCDB_ATTR_PARENT_NAME; + attrib.name = KCDB_ATTRNAME_PARENT_NAME; + attrib.type = KCDB_TYPE_STRING; + LoadString(hinst_kcreddb, IDS_PARENT, sbuf, ARRAYLENGTH(sbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = NULL; + attrib.flags = KCDB_ATTR_FLAG_SYSTEM; + attrib.compute_cb = NULL; + attrib.compute_min_cbsize = 0; + attrib.compute_max_cbsize = 0; + + kcdb_attrib_register(&attrib, NULL); + + /* Issed On */ + attrib.id = KCDB_ATTR_ISSUE; + attrib.name = KCDB_ATTRNAME_ISSUE; + attrib.type = KCDB_TYPE_DATE; + LoadString(hinst_kcreddb, IDS_ISSUED, sbuf, ARRAYLENGTH(sbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = NULL; + attrib.flags = KCDB_ATTR_FLAG_SYSTEM; + attrib.compute_cb = NULL; + attrib.compute_min_cbsize = 0; + attrib.compute_max_cbsize = 0; + + kcdb_attrib_register(&attrib, NULL); + + /* Expires On */ + attrib.id = KCDB_ATTR_EXPIRE; + attrib.name = KCDB_ATTRNAME_EXPIRE; + attrib.type = KCDB_TYPE_DATE; + LoadString(hinst_kcreddb, IDS_EXPIRES, sbuf, ARRAYLENGTH(sbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = NULL; + attrib.flags = KCDB_ATTR_FLAG_SYSTEM; + attrib.compute_cb = NULL; + attrib.compute_min_cbsize = 0; + attrib.compute_max_cbsize = 0; + + kcdb_attrib_register(&attrib, NULL); + + /* Renewable Time Expires On */ + attrib.id = KCDB_ATTR_RENEW_EXPIRE; + attrib.name = KCDB_ATTRNAME_RENEW_EXPIRE; + attrib.type = KCDB_TYPE_DATE; + LoadString(hinst_kcreddb, IDS_RENEW_EXPIRES, + sbuf, ARRAYLENGTH(sbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = NULL; + attrib.flags = KCDB_ATTR_FLAG_SYSTEM; + attrib.compute_cb = NULL; + attrib.compute_min_cbsize = 0; + attrib.compute_max_cbsize = 0; + + kcdb_attrib_register(&attrib, NULL); + + /* Time Left */ + attrib.id = KCDB_ATTR_TIMELEFT; + attrib.alt_id = KCDB_ATTR_EXPIRE; + attrib.name = KCDB_ATTRNAME_TIMELEFT; + attrib.type = KCDB_TYPE_INTERVAL; + LoadString(hinst_kcreddb, IDS_TIMELEFT, sbuf, ARRAYLENGTH(sbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = NULL; + attrib.flags = KCDB_ATTR_FLAG_SYSTEM | + KCDB_ATTR_FLAG_COMPUTED | + KCDB_ATTR_FLAG_ALTVIEW | + KCDB_ATTR_FLAG_VOLATILE; + attrib.compute_cb = kcdb_attr_sys_cb; + attrib.compute_min_cbsize = sizeof(__int64); + attrib.compute_max_cbsize = sizeof(__int64); + + kcdb_attrib_register(&attrib, NULL); + + /* Renewable Time Left */ + attrib.id = KCDB_ATTR_RENEW_TIMELEFT; + attrib.alt_id = KCDB_ATTR_RENEW_EXPIRE; + attrib.name = KCDB_ATTRNAME_RENEW_TIMELEFT; + attrib.type = KCDB_TYPE_INTERVAL; + LoadString(hinst_kcreddb, + IDS_RENEW_TIMELEFT, sbuf, ARRAYLENGTH(sbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = NULL; + attrib.flags = KCDB_ATTR_FLAG_SYSTEM | + KCDB_ATTR_FLAG_COMPUTED | + KCDB_ATTR_FLAG_ALTVIEW | + KCDB_ATTR_FLAG_VOLATILE; + attrib.compute_cb = kcdb_attr_sys_cb; + attrib.compute_min_cbsize = sizeof(__int64); + attrib.compute_max_cbsize = sizeof(__int64); + + kcdb_attrib_register(&attrib, NULL); + + /* Location of Credential */ + attrib.id = KCDB_ATTR_LOCATION; + attrib.name = KCDB_ATTRNAME_LOCATION; + attrib.type = KCDB_TYPE_STRING; + LoadString(hinst_kcreddb, IDS_LOCATION, sbuf, ARRAYLENGTH(sbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = NULL; + attrib.flags = KCDB_ATTR_FLAG_SYSTEM; + attrib.compute_cb = NULL; + attrib.compute_min_cbsize = 0; + attrib.compute_max_cbsize = 0; + + kcdb_attrib_register(&attrib, NULL); + + /* Lifetime */ + attrib.id = KCDB_ATTR_LIFETIME; + attrib.name = KCDB_ATTRNAME_LIFETIME; + attrib.type = KCDB_TYPE_INTERVAL; + LoadString(hinst_kcreddb, IDS_LIFETIME, sbuf, ARRAYLENGTH(sbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = NULL; + attrib.flags = KCDB_ATTR_FLAG_SYSTEM; + attrib.compute_cb = NULL; + attrib.compute_min_cbsize = 0; + attrib.compute_max_cbsize = 0; + + kcdb_attrib_register(&attrib, NULL); + + /* Renewable Lifetime */ + attrib.id = KCDB_ATTR_RENEW_LIFETIME; + attrib.name = KCDB_ATTRNAME_RENEW_LIFETIME; + attrib.type = KCDB_TYPE_INTERVAL; + LoadString(hinst_kcreddb, + IDS_RENEW_LIFETIME, sbuf, ARRAYLENGTH(sbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = NULL; + attrib.flags = KCDB_ATTR_FLAG_SYSTEM; + attrib.compute_cb = NULL; + attrib.compute_min_cbsize = 0; + attrib.compute_max_cbsize = 0; + + kcdb_attrib_register(&attrib, NULL); + + /* Flags */ + attrib.id = KCDB_ATTR_FLAGS; + attrib.name = KCDB_ATTRNAME_FLAGS; + attrib.type = KCDB_TYPE_INT32; + LoadString(hinst_kcreddb, IDS_FLAGS, sbuf, ARRAYLENGTH(sbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = NULL; + attrib.flags = + KCDB_ATTR_FLAG_REQUIRED | + KCDB_ATTR_FLAG_COMPUTED | + KCDB_ATTR_FLAG_SYSTEM | + KCDB_ATTR_FLAG_HIDDEN; + attrib.compute_cb = kcdb_attr_sys_cb; + attrib.compute_min_cbsize = sizeof(khm_int32); + attrib.compute_max_cbsize = sizeof(khm_int32); + + kcdb_attrib_register(&attrib, NULL); +} + +void kcdb_attrib_exit(void) +{ + DeleteCriticalSection(&cs_attrib); + + if(kcdb_attrib_tbl) + free(kcdb_attrib_tbl); + + if(kcdb_property_tbl) + free(kcdb_property_tbl); +} + +KHMEXP khm_int32 KHMAPI kcdb_attrib_get_id(wchar_t *name, khm_int32 * id) +{ + kcdb_attrib_i * ai; + + if(!name) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_attrib); + ai = hash_lookup(kcdb_attrib_namemap, (void *) name); + LeaveCriticalSection(&cs_attrib); + + if(ai) { + *id = ai->attr.id; + return KHM_ERROR_SUCCESS; + } else { + *id = KCDB_ATTR_INVALID; + return KHM_ERROR_NOT_FOUND; + } +} + +KHMEXP khm_int32 KHMAPI kcdb_attrib_register(kcdb_attrib * attrib, khm_int32 * new_id) +{ + kcdb_attrib_i * ai; + size_t cb_name; + size_t cb_short_desc; + size_t cb_long_desc; + khm_int32 attr_id; + khm_boolean prop = FALSE; + + if(!attrib || + KHM_FAILED(kcdb_type_get_info(attrib->type, NULL)) || + !attrib->name) + return KHM_ERROR_INVALID_PARM; + + if(FAILED(StringCbLength(attrib->name, KCDB_MAXCB_NAME, &cb_name))) + return KHM_ERROR_TOO_LONG; + cb_name += sizeof(wchar_t); + + if(attrib->short_desc) { + if(FAILED(StringCbLength(attrib->short_desc, KCDB_MAXCB_SHORT_DESC, &cb_short_desc))) + return KHM_ERROR_TOO_LONG; + cb_short_desc += sizeof(wchar_t); + } else + cb_short_desc = 0; + + if(attrib->long_desc) { + if(FAILED(StringCbLength(attrib->long_desc, KCDB_MAXCB_LONG_DESC, &cb_long_desc))) + return KHM_ERROR_TOO_LONG; + cb_long_desc += sizeof(wchar_t); + } else + cb_long_desc = 0; + + if((attrib->flags & KCDB_ATTR_FLAG_COMPUTED) && + (!attrib->compute_cb || + attrib->compute_min_cbsize <= 0 || + attrib->compute_max_cbsize < attrib->compute_min_cbsize)) + return KHM_ERROR_INVALID_PARM; + + if ((attrib->flags & KCDB_ATTR_FLAG_ALTVIEW) && + KHM_FAILED(kcdb_attrib_get_info(attrib->alt_id, + NULL))) + return KHM_ERROR_INVALID_PARM; + + prop = !!(attrib->flags & KCDB_ATTR_FLAG_PROPERTY); + + EnterCriticalSection(&cs_attrib); + + if( + !prop && + (attrib->id < 0 || attrib->id > KCDB_ATTR_MAX_ID)) + { + if(KHM_FAILED(kcdb_attrib_next_free_id(&attr_id))) { + LeaveCriticalSection(&cs_attrib); + return KHM_ERROR_NO_RESOURCES; + } + } else if ( + prop && + (attrib->id < KCDB_ATTR_MIN_PROP_ID || attrib->id > KCDB_ATTR_MAX_PROP_ID)) + { + if(KHM_FAILED(kcdb_attrib_next_free_prop_id(&attr_id))) { + LeaveCriticalSection(&cs_attrib); + return KHM_ERROR_NO_RESOURCES; + } + } else { + attr_id = attrib->id; + } + +#ifdef DEBUG + assert(!prop || (attr_id >= KCDB_ATTR_MIN_PROP_ID && attr_id <= KCDB_ATTR_MAX_PROP_ID)); + assert(prop || (attr_id >= 0 && attr_id <= KCDB_ATTR_MAX_ID)); +#endif + + if((!prop && kcdb_attrib_tbl[attr_id]) || + (prop && kcdb_property_tbl[attr_id - KCDB_ATTR_MIN_PROP_ID])) + { + LeaveCriticalSection(&cs_attrib); + return KHM_ERROR_DUPLICATE; + } + + ai = malloc(sizeof(kcdb_attrib_i)); + ZeroMemory(ai, sizeof(kcdb_attrib_i)); + + ai->attr.type = attrib->type; + ai->attr.id = attr_id; + ai->attr.alt_id = attrib->alt_id; + ai->attr.flags = attrib->flags; + ai->attr.compute_cb = attrib->compute_cb; + ai->attr.compute_max_cbsize = attrib->compute_max_cbsize; + ai->attr.compute_min_cbsize = attrib->compute_min_cbsize; + ai->attr.name = malloc(cb_name); + StringCbCopy(ai->attr.name, cb_name, attrib->name); + if(cb_short_desc) { + ai->attr.short_desc = malloc(cb_short_desc); + StringCbCopy(ai->attr.short_desc, cb_short_desc, attrib->short_desc); + } + if(cb_long_desc) { + ai->attr.long_desc = malloc(cb_long_desc); + StringCbCopy(ai->attr.long_desc, cb_long_desc, attrib->long_desc); + } + + LINIT(ai); + + if(!prop) + kcdb_attrib_tbl[attr_id] = ai; + else + kcdb_property_tbl[attr_id - KCDB_ATTR_MIN_PROP_ID] = ai; + + LPUSH(&kcdb_attribs, ai); + + hash_add(kcdb_attrib_namemap, (void *) ai->attr.name, ai); + + LeaveCriticalSection(&cs_attrib); + + kcdb_attrib_post_message(KCDB_OP_INSERT, ai); + + if(new_id) + *new_id = attr_id; + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kcdb_attrib_get_info( + khm_int32 id, + kcdb_attrib ** attrib) +{ + kcdb_attrib_i * ai; + khm_boolean prop; + + if(id >= 0 && id <= KCDB_ATTR_MAX_ID) + prop = FALSE; + else if(id >= KCDB_ATTR_MIN_PROP_ID && id <= KCDB_ATTR_MAX_PROP_ID) + prop = TRUE; + else + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_attrib); + if(prop) + ai = kcdb_property_tbl[id - KCDB_ATTR_MIN_PROP_ID]; + else + ai = kcdb_attrib_tbl[id]; + LeaveCriticalSection(&cs_attrib); + + if(ai) { + if(attrib) { + *attrib = &(ai->attr); + kcdb_attrib_hold(ai); + } + return KHM_ERROR_SUCCESS; + } else { + if(attrib) + *attrib = NULL; + return KHM_ERROR_NOT_FOUND; + } +} + +KHMEXP khm_int32 KHMAPI kcdb_attrib_release_info(kcdb_attrib * attrib) +{ + if(attrib) + kcdb_attrib_release((kcdb_attrib_i *) attrib); + return KHM_ERROR_SUCCESS; +} + + +KHMEXP khm_int32 KHMAPI kcdb_attrib_unregister(khm_int32 id) +{ + /*TODO: implement this */ + return KHM_ERROR_NOT_IMPLEMENTED; +} + +KHMEXP khm_int32 KHMAPI kcdb_attrib_describe( + khm_int32 id, + wchar_t * buffer, + khm_size * cbsize, + khm_int32 flags) +{ + kcdb_attrib_i * ai; + size_t cb_size = 0; + khm_boolean prop; + + if(!cbsize) + return KHM_ERROR_INVALID_PARM; + + if(id >= 0 && id <= KCDB_ATTR_MAX_ID) + prop = FALSE; + else if(id >= KCDB_ATTR_MIN_PROP_ID && id <= KCDB_ATTR_MAX_PROP_ID) + prop = TRUE; + + if(prop) + ai = kcdb_property_tbl[id - KCDB_ATTR_MIN_PROP_ID]; + else + ai = kcdb_attrib_tbl[id]; + + if(!ai) + return KHM_ERROR_NOT_FOUND; + + if((flags & KCDB_TS_SHORT) && + ai->attr.short_desc) + { + if(FAILED(StringCbLength(ai->attr.short_desc, KCDB_MAXCB_SHORT_DESC, &cb_size))) + return KHM_ERROR_UNKNOWN; + cb_size += sizeof(wchar_t); + + if(!buffer || *cbsize < cb_size) { + *cbsize = cb_size; + return KHM_ERROR_TOO_LONG; + } + + StringCbCopy(buffer, *cbsize, ai->attr.short_desc); + + *cbsize = cb_size; + + return KHM_ERROR_SUCCESS; + } else { + if(FAILED(StringCbLength(ai->attr.long_desc, KCDB_MAXCB_LONG_DESC, &cb_size))) + return KHM_ERROR_UNKNOWN; + cb_size += sizeof(wchar_t); + + if(!buffer || *cbsize < cb_size) { + *cbsize = cb_size; + return KHM_ERROR_TOO_LONG; + } + + StringCbCopy(buffer, *cbsize, ai->attr.long_desc); + + *cbsize = cb_size; + + return KHM_ERROR_SUCCESS; + } +} + +khm_int32 kcdb_attrib_next_free_prop_id(khm_int32 * id) +{ + int i; + + if(!id) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_attrib); + for(i=0;i < KCDB_ATTR_MAX_PROPS; i++) { + if(!kcdb_property_tbl[i]) + break; + } + LeaveCriticalSection(&cs_attrib); + + if(i < KCDB_ATTR_MAX_PROPS) { + *id = i + KCDB_ATTR_MIN_PROP_ID; + return KHM_ERROR_SUCCESS; + } else { + *id = KCDB_ATTR_INVALID; + return KHM_ERROR_NO_RESOURCES; + } +} + +khm_int32 kcdb_attrib_next_free_id(khm_int32 * id) +{ + int i; + + if(!id) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_attrib); + for(i=0;i<= KCDB_ATTR_MAX_ID; i++) { + if(!kcdb_attrib_tbl[i]) + break; + } + LeaveCriticalSection(&cs_attrib); + + if(i <= KCDB_ATTR_MAX_ID) { + *id = i; + return KHM_ERROR_SUCCESS; + } else { + *id = KCDB_ATTR_INVALID; + return KHM_ERROR_NO_RESOURCES; + } +} + +KHMEXP khm_int32 KHMAPI kcdb_attrib_get_count( + khm_int32 and_flags, + khm_int32 eq_flags, + khm_size * pcount) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + khm_size count = 0; + int i; + + if(pcount == NULL) + return KHM_ERROR_INVALID_PARM; + + eq_flags &= and_flags; + + EnterCriticalSection(&cs_attrib); + for(i = 0; i <= KCDB_ATTR_MAX_ID; i++) { + if(kcdb_attrib_tbl[i] && + (kcdb_attrib_tbl[i]->attr.flags & and_flags) == eq_flags) + count++; + } + + for(i = 0; i < KCDB_ATTR_MAX_PROPS; i++) { + if(kcdb_property_tbl[i] && + (kcdb_property_tbl[i]->attr.flags & and_flags) == eq_flags) + count++; + } + LeaveCriticalSection(&cs_attrib); + + *pcount = count; + + return rv; +} + +KHMEXP khm_int32 KHMAPI kcdb_attrib_get_ids( + khm_int32 and_flags, + khm_int32 eq_flags, + khm_int32 * plist, + khm_size * pcsize) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + khm_size count = 0; + int i; + + if(plist == NULL || pcsize == NULL) + return KHM_ERROR_INVALID_PARM; + + eq_flags &= and_flags; + + EnterCriticalSection(&cs_attrib); + for(i = 0; i <= KCDB_ATTR_MAX_ID; i++) { + if(kcdb_attrib_tbl[i] && + (kcdb_attrib_tbl[i]->attr.flags & and_flags) == eq_flags) { + if(count >= *pcsize) { + rv = KHM_ERROR_TOO_LONG; + count++; + } else + plist[count++] = i; + } + } + + for(i = 0; i < KCDB_ATTR_MAX_PROPS; i++) { + if(kcdb_property_tbl[i] && + (kcdb_property_tbl[i]->attr.flags & and_flags) == eq_flags) { + if(count >= *pcsize) { + rv = KHM_ERROR_TOO_LONG; + count++; + } else + plist[count++] = i + KCDB_ATTR_MIN_PROP_ID; + } + } + LeaveCriticalSection(&cs_attrib); + + *pcsize = count; + + return rv; +} diff --git a/src/windows/identity/kcreddb/attrib.h b/src/windows/identity/kcreddb/attrib.h new file mode 100644 index 000000000..5199aec92 --- /dev/null +++ b/src/windows/identity/kcreddb/attrib.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KCDB_ATTRIB_H +#define __KHIMAIRA_KCDB_ATTRIB_H + +/* Attributes */ + +typedef struct kcdb_attrib_i_t { + kcdb_attrib attr; + + khm_int32 refcount; + + struct kcdb_attrib_i_t * next; + struct kcdb_attrib_i_t * prev; +} kcdb_attrib_i; + +#define KCDB_ATTRIB_HASH_SIZE 31 + +void kcdb_attrib_init(void); +void kcdb_attrib_exit(void); +void kcdb_attrib_add_ref_func(const void * key, void * va); +void kcdb_attrib_del_ref_func(const void * key, void * va); +void kcdb_attrib_msg_completion(kmq_message * m); +khm_int32 kcdb_attrib_next_free_prop_id(khm_int32 * id); +khm_int32 kcdb_attrib_next_free_id(khm_int32 * id); +khm_int32 kcdb_attrib_hold(kcdb_attrib_i * ai); +khm_int32 kcdb_attrib_release(kcdb_attrib_i * ai); +void kcdb_attrib_post_message(khm_int32 op, kcdb_attrib_i * ai); +khm_int32 KHMAPI kcdb_attr_sys_cb(khm_handle cred, khm_int32 attr, void * buf, khm_size * pcb_buf); + +#endif \ No newline at end of file diff --git a/src/windows/identity/kcreddb/buf.c b/src/windows/identity/kcreddb/buf.c new file mode 100644 index 000000000..0f50be25d --- /dev/null +++ b/src/windows/identity/kcreddb/buf.c @@ -0,0 +1,391 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include + +void kcdb_buf_new(kcdb_buf * buf, khm_size n_fields) +{ + buf->buffer = malloc(KCDB_BUF_CBBUF_INITIAL); + buf->cb_buffer = KCDB_BUF_CBBUF_INITIAL; + buf->cb_used = 0; + + if(n_fields == KCDB_BUF_DEFAULT) + n_fields = KCDB_BUF_FIELDS_INITIAL; + + assert(n_fields < KCDB_BUF_MAX_SLOTS); + + buf->n_fields = n_fields; + buf->nc_fields = UBOUNDSS(n_fields, KCDB_BUF_FIELDS_INITIAL, KCDB_BUF_FIELDS_GROWTH); + buf->fields = malloc(sizeof(buf->fields[0]) * buf->n_fields); + ZeroMemory(buf->fields, sizeof(buf->fields[0]) * buf->n_fields); +} + +void kcdb_buf_delete(kcdb_buf * buf) +{ + buf->cb_buffer = 0; + buf->cb_used = 0; + if(buf->buffer) + free(buf->buffer); + buf->buffer = NULL; + + buf->n_fields = 0; + buf->nc_fields = 0; + if(buf->fields) + free(buf->fields); + buf->fields = NULL; +} + +static void kcdb_buf_assert_size(kcdb_buf * buf, khm_size cbsize) +{ + khm_size new_size; + void * new_buf; + + /* should be less than or equal to the max signed 32 bit int */ + assert(cbsize <= KHM_INT32_MAX); + if(cbsize <= buf->cb_buffer) + return; + + new_size = UBOUNDSS(cbsize, KCDB_BUF_CBBUF_INITIAL, KCDB_BUF_CBBUF_GROWTH); + + assert(new_size > buf->cb_buffer && new_size > 0); + + new_buf = malloc(new_size); + assert(new_buf != NULL); + + memcpy(new_buf, buf->buffer, buf->cb_used); + free(buf->buffer); + buf->buffer = new_buf; +} + +void kcdb_buf_alloc(kcdb_buf * buf, khm_size slot, khm_ui_2 id, khm_size cbsize) +{ + khm_size cbnew; + khm_ssize cbdelta; + khm_size cbold; + kcdb_buf_field * f; + + cbnew = UBOUND32(cbsize); + + assert(slot <= KCDB_BUF_APPEND); + + if(slot == KCDB_BUF_APPEND) { + slot = kcdb_buf_slot_by_id(buf, id); + if(slot == KCDB_BUF_INVALID_SLOT) + slot = buf->n_fields; + } + + assert(slot < KCDB_BUF_MAX_SLOTS); + + if((slot + 1) > buf->nc_fields) { + kcdb_buf_field * nf; + khm_size ns; + + ns = UBOUNDSS((slot + 1), KCDB_BUF_FIELDS_INITIAL, KCDB_BUF_FIELDS_GROWTH); + + nf = malloc(sizeof(buf->fields[0]) * ns); + memcpy(nf, buf->fields, sizeof(buf->fields[0]) * buf->n_fields); + + if(ns > buf->n_fields) + memset(&(nf[buf->n_fields]), 0, sizeof(buf->fields[0]) * (ns - buf->n_fields)); + + free(buf->fields); + buf->fields = nf; + buf->nc_fields = ns; + } + + if((slot + 1) > buf->n_fields) + buf->n_fields = slot + 1; + + f = &(buf->fields[slot]); + + if(f->flags & KCDB_CREDF_FLAG_ALLOCD) { + /* there's already an allocation. we have to resize it to + accomodate the new size */ + cbold = UBOUND32(f->cbsize); + /* demote before substraction */ + cbdelta = ((khm_ssize) cbnew) - (khm_ssize) cbold; + + if(cbnew > cbold) { + kcdb_buf_assert_size(buf, buf->cb_used + cbdelta); + } + + if(buf->cb_used > f->offset + cbold) { + int i; + + memmove( + ((BYTE *) buf->buffer) + (f->offset + cbnew), + ((BYTE *) buf->buffer) + (f->offset + cbold), + buf->cb_used - (f->offset + cbold)); + + for(i=0; i < (int) buf->n_fields; i++) { + if(i != slot && + (buf->fields[i].flags & KCDB_CREDF_FLAG_ALLOCD) && + buf->fields[i].offset > f->offset) + { + buf->fields[i].offset = + (khm_ui_4)(((khm_ssize) buf->fields[i].offset) + cbdelta); + } + } + } + + /* demote integer before adding signed quantity */ + buf->cb_used = (khm_size)(((khm_ssize) buf->cb_used) + cbdelta); + + f->cbsize = (khm_ui_4) cbsize; + + } else { + kcdb_buf_assert_size(buf, buf->cb_used + cbnew); + f->offset = (khm_ui_4) buf->cb_used; + f->cbsize = (khm_ui_4) cbsize; + buf->cb_used += cbnew; + } + + if(cbsize == 0) { + f->flags &= ~KCDB_CREDF_FLAG_ALLOCD; + f->flags &= ~KCDB_CREDF_FLAG_DATA; + f->id = KCDB_BUFF_ID_INVALID; + } else { + f->flags |= KCDB_CREDF_FLAG_ALLOCD; + f->id = id; + } +} + +void kcdb_buf_dup(kcdb_buf * dest, const kcdb_buf * src) +{ + khm_size cb_buf; + khm_size nc_fields; + + cb_buf = UBOUNDSS(src->cb_used, KCDB_BUF_CBBUF_INITIAL, KCDB_BUF_CBBUF_GROWTH); +#if 0 + /* replaced by UBOUNDSS() above */ + (src->cb_used <= kcdb_cred_initial_size)? kcdb_cred_initial_size: + kcdb_cred_initial_size + + (((src->cb_used - (kcdb_cred_initial_size + 1)) / kcdb_cred_growth_factor + 1) * kcdb_cred_growth_factor); +#endif + + kcdb_buf_delete(dest); + + dest->cb_buffer = cb_buf; + dest->cb_used = src->cb_used; + dest->buffer = malloc(cb_buf); + memcpy(dest->buffer, src->buffer, src->cb_used); + + nc_fields = UBOUNDSS(src->n_fields, KCDB_BUF_FIELDS_INITIAL, KCDB_BUF_FIELDS_GROWTH); + dest->nc_fields = nc_fields; + dest->n_fields = src->n_fields; + dest->fields = malloc(nc_fields * sizeof(dest->fields[0])); + memcpy(dest->fields, src->fields, src->n_fields * sizeof(dest->fields[0])); + if(dest->n_fields < dest->nc_fields) + memset(&(dest->fields[dest->n_fields]), 0, (src->nc_fields - src->n_fields) * sizeof(dest->fields[0])); +} + +void kcdb_buf_set_value(kcdb_buf * buf, khm_size slot, khm_ui_2 id, void * src, khm_size cb_src) +{ + void * dest; + kcdb_buf_alloc(buf, slot, id, cb_src); + if(slot == KCDB_BUF_APPEND) { + slot = kcdb_buf_slot_by_id(buf, id); + if(slot == KCDB_BUF_INVALID_SLOT) { +#ifdef DEBUG + assert(FALSE); +#else + return; +#endif + } + } + if(kcdb_buf_exist(buf, slot)) { + dest = kcdb_buf_get(buf, slot); + memcpy(dest, src, cb_src); + + buf->fields[slot].flags |= KCDB_CREDF_FLAG_DATA; + } +} + +int kcdb_buf_exist(kcdb_buf * buf, khm_size slot) +{ + if(slot >= buf->n_fields) + return 0; + return (buf->fields[slot].flags & KCDB_CREDF_FLAG_ALLOCD); +} + +int kcdb_buf_val_exist(kcdb_buf * buf, khm_size slot) +{ + if(slot >= buf->n_fields) + return 0; + return (buf->fields[slot].flags & KCDB_CREDF_FLAG_DATA); +} + +void * kcdb_buf_get(kcdb_buf * buf, khm_size slot) +{ + if(slot >= buf->n_fields || + !(buf->fields[slot].flags & KCDB_CREDF_FLAG_ALLOCD)) + return NULL; + return (((BYTE *) buf->buffer) + buf->fields[slot].offset); +} + +khm_size kcdb_buf_size(kcdb_buf * buf, khm_size slot) +{ + if(slot >= buf->n_fields || + !(buf->fields[slot].flags & KCDB_CREDF_FLAG_ALLOCD)) + return 0; + return (buf->fields[slot].cbsize); +} + +void kcdb_buf_set_value_flag(kcdb_buf * buf, khm_size slot) +{ + if(slot >= buf->n_fields || + !(buf->fields[slot].flags & KCDB_CREDF_FLAG_ALLOCD)) + return; + + (buf->fields[slot].flags |= KCDB_CREDF_FLAG_DATA); +} + +khm_size kcdb_buf_slot_by_id(kcdb_buf * buf, khm_ui_2 id) +{ + int i; + + for(i=0; i < (int) buf->n_fields; i++) { + if(buf->fields[i].id == id) + break; + } + + if(i < (int) buf->n_fields) + return i; + else + return KCDB_BUF_INVALID_SLOT; +} + +/* API for accessing generic buffers */ + +KHMEXP khm_int32 KHMAPI kcdb_buf_get_attr( + khm_handle record, + khm_int32 attr_id, + khm_int32 * attr_type, + void * buffer, + khm_size * pcb_buf) +{ + if(kcdb_cred_is_active_cred(record)) + return kcdb_cred_get_attr(record, attr_id, attr_type, buffer, pcb_buf); + else if(kcdb_is_active_identity(record)) + return kcdb_identity_get_attr(record, attr_id, attr_type, buffer, pcb_buf); + else + return KHM_ERROR_INVALID_PARM; +} + +KHMEXP khm_int32 KHMAPI kcdb_buf_get_attrib( + khm_handle record, + wchar_t * attr_name, + khm_int32 * attr_type, + void * buffer, + khm_size * pcb_buf) +{ + if(kcdb_cred_is_active_cred(record)) + return kcdb_cred_get_attrib(record, attr_name, attr_type, buffer, pcb_buf); + else if(kcdb_is_active_identity(record)) + return kcdb_identity_get_attrib(record, attr_name, attr_type, buffer, pcb_buf); + else + return KHM_ERROR_INVALID_PARM; +} + +KHMEXP khm_int32 KHMAPI kcdb_buf_get_attr_string( + khm_handle record, + khm_int32 attr_id, + wchar_t * buffer, + khm_size * pcbbuf, + khm_int32 flags) +{ + if(kcdb_cred_is_active_cred(record)) + return kcdb_cred_get_attr_string(record, attr_id, buffer, pcbbuf, flags); + else if(kcdb_is_active_identity(record)) + return kcdb_identity_get_attr_string(record, attr_id, buffer, pcbbuf, flags); + else + return KHM_ERROR_INVALID_PARM; +} + +KHMEXP khm_int32 KHMAPI kcdb_buf_get_attrib_string( + khm_handle record, + wchar_t * attr_name, + wchar_t * buffer, + khm_size * pcbbuf, + khm_int32 flags) +{ + if(kcdb_cred_is_active_cred(record)) + return kcdb_cred_get_attrib_string(record, attr_name, buffer, pcbbuf, flags); + else if(kcdb_is_active_identity(record)) + return kcdb_identity_get_attrib_string(record, attr_name, buffer, pcbbuf, flags); + else + return KHM_ERROR_INVALID_PARM; +} + +KHMEXP khm_int32 KHMAPI kcdb_buf_set_attr( + khm_handle record, + khm_int32 attr_id, + void * buffer, + khm_size cbbuf) +{ + if(kcdb_cred_is_active_cred(record)) + return kcdb_cred_set_attr(record, attr_id, buffer, cbbuf); + else if(kcdb_is_active_identity(record)) + return kcdb_identity_set_attr(record, attr_id, buffer, cbbuf); + else + return KHM_ERROR_INVALID_PARM; +} + +KHMEXP khm_int32 KHMAPI kcdb_buf_set_attrib( + khm_handle record, + wchar_t * attr_name, + void * buffer, + khm_size cbbuf) +{ + if(kcdb_cred_is_active_cred(record)) + return kcdb_cred_set_attrib(record, attr_name, buffer, cbbuf); + else if(kcdb_is_active_identity(record)) + return kcdb_identity_set_attrib(record, attr_name, buffer, cbbuf); + else + return KHM_ERROR_INVALID_PARM; +} + +KHMEXP khm_int32 KHMAPI kcdb_buf_hold(khm_handle record) +{ + if(kcdb_cred_is_active_cred(record)) + return kcdb_cred_hold(record); + else if(kcdb_is_active_identity(record)) + return kcdb_identity_hold(record); + else + return KHM_ERROR_INVALID_PARM; +} + +KHMEXP khm_int32 KHMAPI kcdb_buf_release(khm_handle record) +{ + if(kcdb_cred_is_active_cred(record)) + return kcdb_cred_release(record); + else if(kcdb_is_active_identity(record)) + return kcdb_identity_release(record); + else + return KHM_ERROR_INVALID_PARM; +} + diff --git a/src/windows/identity/kcreddb/buf.h b/src/windows/identity/kcreddb/buf.h new file mode 100644 index 000000000..3ff1f041d --- /dev/null +++ b/src/windows/identity/kcreddb/buf.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KCDB_BUF_H +#define __KHIMAIRA_KCDB_BUF_H + +typedef struct tag_kcdb_buf_field { + khm_ui_2 id; + khm_ui_2 flags; + khm_ui_4 offset; + khm_ui_4 cbsize; +} kcdb_buf_field; + +#define KCDB_CREDF_FLAG_EMPTY 0 +#define KCDB_CREDF_FLAG_DATA 1 +#define KCDB_CREDF_FLAG_INLINE 2 +#define KCDB_CREDF_FLAG_ALLOCD 4 + +#define KCDB_BUFF_ID_INVALID 0xffff + +typedef struct tag_kcdb_buf { + void * buffer; + khm_size cb_buffer; + khm_size cb_used; + + kcdb_buf_field * fields; + khm_size n_fields; + khm_size nc_fields; +} kcdb_buf; + +#define KCDB_BUF_CBBUF_INITIAL 4096 +#define KCDB_BUF_CBBUF_GROWTH 4096 +#define KCDB_BUF_FIELDS_INITIAL 16 +#define KCDB_BUF_FIELDS_GROWTH 16 + +#define KCDB_BUF_APPEND 0x8000 + +#define KCDB_BUF_INVALID_SLOT 0xf0000000 +#define KCDB_BUF_DEFAULT 0xe0000000 + +#define KCDB_BUF_MAX_SLOTS 0x00004000 + +void kcdb_buf_new(kcdb_buf * buf, khm_size n_slots); +void kcdb_buf_delete(kcdb_buf * buf); +void kcdb_buf_alloc(kcdb_buf * buf, khm_size slot, khm_ui_2 id, khm_size cbsize); +void kcdb_buf_dup(kcdb_buf * dest, const kcdb_buf * src); +void kcdb_buf_set_value(kcdb_buf * buf, khm_size slot, khm_ui_2 id, void * src, khm_size cb_src); +int kcdb_buf_exist(kcdb_buf * buf, khm_size slot); +int kcdb_buf_val_exist(kcdb_buf * buf, khm_size slot); +void * kcdb_buf_get(kcdb_buf * buf, khm_size slot); +khm_size kcdb_buf_size(kcdb_buf * buf, khm_size slot); +void kcdb_buf_set_value_flag(kcdb_buf * buf, khm_size slot); +khm_size kcdb_buf_slot_by_id(kcdb_buf * buf, khm_ui_2 id); + +#endif diff --git a/src/windows/identity/kcreddb/credential.c b/src/windows/identity/kcreddb/credential.c new file mode 100644 index 000000000..1fe1dcd97 --- /dev/null +++ b/src/windows/identity/kcreddb/credential.c @@ -0,0 +1,1047 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include + +/* cs_creds protects the *collection* of credentials, while l_creds + protects the *contents* of individual credentials. */ +CRITICAL_SECTION cs_creds; +kcdb_cred * kcdb_creds = NULL; + +/* a read lock must be obtained when querying any existing credential. + a write lock must be obtained when modifying any existing credential. + */ +RWLOCK l_creds; + +/* serial number */ +khm_ui_8 kcdb_cred_id = 0; + +void kcdb_cred_init(void) +{ + InitializeCriticalSection(&cs_creds); + InitializeRwLock(&l_creds); + kcdb_cred_id = 0; +} + +void kcdb_cred_exit(void) +{ + /*TODO: Free the credentials */ + DeleteCriticalSection(&cs_creds); + DeleteRwLock(&l_creds); +} + +/*! \internal + + can be called by kcdb_cred_dup with a write lock on l_creds and in other + places with a read lock on l_creds. New credentials must be creatable while + holding either lock. */ +KHMEXP khm_int32 KHMAPI kcdb_cred_create( + wchar_t * name, + khm_handle identity, + khm_int32 cred_type, + khm_handle * result) +{ + kcdb_cred * cred; + size_t cb_name; + + if(!name || !result || + FAILED(StringCbLength(name, KCDB_CRED_MAXCB_NAME, &cb_name)) || + KHM_FAILED(kcdb_credtype_get_info(cred_type, NULL)) || + KHM_FAILED(kcdb_identity_hold(identity)) + ) + { + return KHM_ERROR_INVALID_PARM; + } + + cb_name += sizeof(wchar_t); + + cred = malloc(sizeof(kcdb_cred)); + ZeroMemory(cred, sizeof(kcdb_cred)); + + cred->magic = KCDB_CRED_MAGIC; + cred->identity = identity; + cred->name = malloc(cb_name); + StringCbCopy(cred->name, cb_name, name); + cred->type = cred_type; + + cred->refcount = 1; /* initially held */ + + LINIT(cred); + + kcdb_buf_new(&cred->buf, KCDB_ATTR_MAX_ID + 1); + + /* Not obtaining a write lock on l_cred on purpose. + Well, because no one should be referencing this credential until + this function returns. */ + EnterCriticalSection(&cs_creds); + cred->id = kcdb_cred_id++; + LPUSH(&kcdb_creds, cred); + LeaveCriticalSection(&cs_creds); + + *result = cred; + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kcdb_cred_update( + khm_handle vdest, + khm_handle vsrc) +{ + khm_int32 rv = KHM_ERROR_EQUIVALENT; + kcdb_cred * src; + kcdb_cred * dest; + kcdb_type * t; + kcdb_attrib * a; + void * srcbuf; + void * destbuf; + khm_size cbsrcbuf; + khm_size cbdestbuf; + + int i; + + kcdb_cred_lock_write(); + + if(!kcdb_cred_is_active_cred(vsrc) || + !kcdb_cred_is_active_cred(vdest)) + goto _exit; + + src = (kcdb_cred *) vsrc; + dest = (kcdb_cred *) vdest; + + for(i=0;iflags & KCDB_ATTR_FLAG_COMPUTED) || + KHM_FAILED(kcdb_type_get_info(a->type, &t))) { + kcdb_attrib_release_info(a); + continue; + } + + srcbuf = kcdb_cred_buf_get(src,i); + cbsrcbuf = kcdb_cred_buf_size(src, i); + + if(kcdb_cred_val_exist(dest, i)) { + destbuf = kcdb_cred_buf_get(dest, i); + cbdestbuf = kcdb_cred_buf_size(dest, i); + + if(!t->comp(srcbuf, cbsrcbuf, destbuf, cbdestbuf)) + goto _skip_copy; + } + + kcdb_buf_set_value(&dest->buf, i, i, srcbuf, cbsrcbuf); + rv = KHM_ERROR_SUCCESS; + +_skip_copy: + kcdb_attrib_release_info(a); + kcdb_type_release_info(t); + } + } + + if (dest->flags != src->flags) { + khm_int32 old_flags; + + old_flags = dest->flags; + + dest->flags = (src->flags & ~KCDB_CRED_FLAGMASK_ADDITIVE) | + ((src->flags | dest->flags) & KCDB_CRED_FLAGMASK_ADDITIVE); + + if (dest->flags != old_flags) + rv = KHM_ERROR_SUCCESS; + } + +_exit: + kcdb_cred_unlock_write(); + return rv; +} + +KHMEXP khm_int32 KHMAPI kcdb_cred_dup( + khm_handle vcred, + khm_handle * pnewcred) +{ + khm_int32 code = KHM_ERROR_SUCCESS; + kcdb_cred * cred; + kcdb_cred * newcred; + khm_handle vnewcred; + + if(!pnewcred) + return KHM_ERROR_INVALID_PARM; + + *pnewcred = NULL; + + kcdb_cred_lock_write(); + + if(!kcdb_cred_is_active_cred(vcred)) { + code = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + cred = (kcdb_cred *) vcred; + + if(KHM_FAILED(kcdb_cred_create( + cred->name, + cred->identity, + cred->type, + &vnewcred))) + { + code = KHM_ERROR_UNKNOWN; + goto _exit; + } + + newcred = (kcdb_cred *) vnewcred; + + newcred->flags = cred->flags; + + kcdb_buf_dup(&newcred->buf, &cred->buf); + + /* newcred is already held from the call to kcdb_cred_create */ + *pnewcred = (khm_handle) newcred; + +_exit: + kcdb_cred_unlock_write(); + return code; +} + +KHMEXP khm_int32 KHMAPI kcdb_cred_get_serial( + khm_handle vcred, + khm_ui_8 * pserial) +{ + kcdb_cred * c; + + if(!pserial) + return KHM_ERROR_INVALID_PARM; + + LockObtainRead(&l_creds); + + if(!kcdb_cred_is_active_cred(vcred)) { + LockReleaseRead(&l_creds); + return KHM_ERROR_INVALID_PARM; + } + + c = (kcdb_cred *) vcred; + + *pserial = c->id; + + LockReleaseRead(&l_creds); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kcdb_cred_set_identity( + khm_handle vcred, + khm_handle id) +{ + kcdb_cred * c; + + if(!kcdb_is_identity(id)) + return KHM_ERROR_INVALID_PARM; + + kcdb_cred_lock_write(); + if(!kcdb_cred_is_active_cred(vcred)) { + kcdb_cred_unlock_write(); + return KHM_ERROR_INVALID_PARM; + } + + c = (kcdb_cred *) vcred; + + if(c->identity) { + kcdb_identity_release((khm_handle) c->identity); + } + kcdb_identity_hold(id); + c->identity = (kcdb_identity *) id; + + kcdb_cred_unlock_write(); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kcdb_cred_get_type( + khm_handle vcred, + khm_int32 * type) +{ + kcdb_cred * c; + + if(!type) + return KHM_ERROR_INVALID_PARM; + + LockObtainRead(&l_creds); + + if(!kcdb_cred_is_active_cred(vcred)) { + LockReleaseRead(&l_creds); + return KHM_ERROR_INVALID_PARM; + } + + c = (kcdb_cred *) vcred; + + *type = c->type; + + LockReleaseRead(&l_creds); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kcdb_cred_set_attrib( + khm_handle cred, + wchar_t * name, + void * buffer, + khm_size cbbuf) +{ + khm_int32 attr_id = -1; + + if(KHM_FAILED(kcdb_attrib_get_id(name, &attr_id))) + return KHM_ERROR_INVALID_PARM; + + return kcdb_cred_set_attr( + cred, + attr_id, + buffer, + cbbuf); +} + +KHMEXP khm_int32 KHMAPI kcdb_cred_set_attr( + khm_handle vcred, + khm_int32 attr_id, + void * buffer, + khm_size cbbuf) +{ + kcdb_cred * cred; + kcdb_type * type = NULL; + kcdb_attrib * attrib = NULL; + khm_size cbdest; + khm_int32 code = KHM_ERROR_SUCCESS; + + if(attr_id < 0 || attr_id > KCDB_ATTR_MAX_ID) + return KHM_ERROR_INVALID_PARM; + + kcdb_cred_lock_write(); + + if(!kcdb_cred_is_active_cred(vcred)) { + kcdb_cred_unlock_write(); + return KHM_ERROR_INVALID_PARM; + } + + cred = (kcdb_cred *) vcred; + + if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) { + kcdb_cred_unlock_write(); + return KHM_ERROR_INVALID_PARM; + } + + if(attrib->flags & KCDB_ATTR_FLAG_COMPUTED) + { + kcdb_cred_unlock_write(); + kcdb_attrib_release_info(attrib); + return KHM_ERROR_INVALID_OPERATION; + } + + if (buffer == 0) { + /* we are removing the value */ + kcdb_buf_alloc(&cred->buf, attr_id, attr_id, 0); + code = KHM_ERROR_SUCCESS; + goto _exit; + } + + if(KHM_FAILED(kcdb_type_get_info(attrib->type, &type))) { + kcdb_cred_unlock_write(); + kcdb_attrib_release_info(attrib); + return KHM_ERROR_INVALID_PARM; + } + + if(!(type->isValid(buffer,cbbuf))) { + code = KHM_ERROR_TYPE_MISMATCH; + goto _exit; + } + + if((type->dup(buffer, cbbuf, NULL, &cbdest)) != KHM_ERROR_TOO_LONG) { + code = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + kcdb_buf_alloc(&cred->buf, attr_id, attr_id, cbdest); + if(!kcdb_cred_buf_exist(cred, attr_id)) { + code = KHM_ERROR_NO_RESOURCES; + goto _exit; + } + + if(KHM_FAILED(code = + type->dup(buffer, cbbuf, kcdb_cred_buf_get(cred,attr_id), &cbdest))) + { + kcdb_buf_alloc(&cred->buf, attr_id, attr_id, 0); + goto _exit; + } + + kcdb_buf_set_value_flag(&cred->buf, attr_id); + +_exit: + kcdb_cred_unlock_write(); + + if(attrib) + kcdb_attrib_release_info(attrib); + if(type) + kcdb_type_release_info(type); + + return code; +} + +KHMEXP khm_int32 KHMAPI kcdb_cred_get_attrib( + khm_handle cred, + wchar_t * name, + khm_int32 * attr_type, + void * buffer, + khm_size * cbbuf) +{ + khm_int32 attr_id = -1; + + if(KHM_FAILED(kcdb_attrib_get_id(name, &attr_id))) + return KHM_ERROR_NOT_FOUND; + + return kcdb_cred_get_attr( + cred, + attr_id, + attr_type, + buffer, + cbbuf); +} + +KHMEXP khm_int32 KHMAPI kcdb_cred_get_attrib_string( + khm_handle cred, + wchar_t * name, + wchar_t * buffer, + khm_size * cbbuf, + khm_int32 flags) +{ + khm_int32 attr_id = -1; + + if(KHM_FAILED(kcdb_attrib_get_id(name, &attr_id))) + return KHM_ERROR_NOT_FOUND; + + return kcdb_cred_get_attr_string( + cred, + attr_id, + buffer, + cbbuf, + flags); +} + +KHMEXP khm_int32 KHMAPI kcdb_cred_get_attr( + khm_handle vcred, + khm_int32 attr_id, + khm_int32 * attr_type, + void * buffer, + khm_size * pcbbuf) +{ + khm_int32 code = KHM_ERROR_SUCCESS; + kcdb_cred * cred = NULL; + kcdb_attrib * attrib = NULL; + kcdb_type * type = NULL; + + if(attr_id < 0 || attr_id > KCDB_ATTR_MAX_ID) + return KHM_ERROR_INVALID_PARM; + + if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) { + return KHM_ERROR_INVALID_PARM; + } + + if(KHM_FAILED(kcdb_type_get_info(attrib->type, &type))) { + kcdb_attrib_release_info(attrib); + return KHM_ERROR_UNKNOWN; + } + + if(attr_type) + *attr_type = attrib->type; + + LockObtainRead(&l_creds); + if(!kcdb_cred_is_active_cred(vcred)) { + code = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + cred = (kcdb_cred *) vcred; + + if(!buffer && !pcbbuf) { + /* in this case the caller is only trying to determine if the + field contains data. We assume that computed fields are + always non-null. */ + code = (kcdb_cred_val_exist(cred, attr_id) || + (attrib->flags & KCDB_ATTR_FLAG_COMPUTED))?KHM_ERROR_SUCCESS:KHM_ERROR_NOT_FOUND; + goto _exit; + } + + if(attrib->flags & KCDB_ATTR_FLAG_COMPUTED) { + code = attrib->compute_cb( + vcred, + attr_id, + buffer, + pcbbuf); + } else if (kcdb_cred_val_exist(cred, attr_id)) { + code = type->dup( + kcdb_cred_buf_get(cred, attr_id), + kcdb_cred_buf_size(cred, attr_id), + buffer, + pcbbuf); + } else { + code = KHM_ERROR_NOT_FOUND; + } + +_exit: + LockReleaseRead(&l_creds); + if(type) + kcdb_type_release_info(type); + if(attrib) + kcdb_attrib_release_info(attrib); + + return code; +} + +KHMEXP khm_int32 KHMAPI kcdb_cred_get_attr_string( + khm_handle vcred, + khm_int32 attr_id, + wchar_t * buffer, + khm_size * pcbbuf, + khm_int32 flags) +{ + khm_int32 code = KHM_ERROR_SUCCESS; + kcdb_cred * cred = NULL; + kcdb_attrib * attrib = NULL; + kcdb_type * type = NULL; + + if(attr_id < 0 || attr_id > KCDB_ATTR_MAX_ID) + return KHM_ERROR_INVALID_PARM; + + if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) { + code = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + if(KHM_FAILED(kcdb_type_get_info(attrib->type, &type))) { + code = KHM_ERROR_UNKNOWN; + goto _exit; + } + + LockObtainRead(&l_creds); + if(!kcdb_cred_is_active_cred(vcred)) { + code = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + cred = (kcdb_cred *) vcred; + + if(!buffer && !pcbbuf) { + /* in this case the caller is only trying to determine if the field + contains data. We assume that computed fields are always non-null. */ + code = (kcdb_cred_val_exist(cred, attr_id) || + (attrib->flags & KCDB_ATTR_FLAG_COMPUTED))?KHM_ERROR_SUCCESS:KHM_ERROR_NOT_FOUND; + goto _exit; + } + + if(attrib->flags & KCDB_ATTR_FLAG_COMPUTED) { + void * buf; + khm_size cbbuf; + + code = attrib->compute_cb( + vcred, + attr_id, + NULL, + &cbbuf); + if(code == KHM_ERROR_TOO_LONG) { + buf = malloc(cbbuf); + code = attrib->compute_cb( + vcred, + attr_id, + buf, + &cbbuf); + if(KHM_SUCCEEDED(code)) { + code = type->toString( + buf, + cbbuf, + buffer, + pcbbuf, + flags); + } + free(buf); + } + } else { + if(kcdb_cred_buf_exist(cred, attr_id)) { + code = type->toString( + kcdb_cred_buf_get(cred, attr_id), + kcdb_cred_buf_size(cred, attr_id), + buffer, + pcbbuf, + flags); + } else + code = KHM_ERROR_NOT_FOUND; + } + +_exit: + LockReleaseRead(&l_creds); + if(type) + kcdb_type_release_info(type); + if(attrib) + kcdb_attrib_release_info(attrib); + + return code; +} + + +KHMEXP khm_int32 KHMAPI kcdb_cred_get_name( + khm_handle vcred, + wchar_t * buffer, + khm_size * cbbuf) +{ + khm_int32 code = KHM_ERROR_SUCCESS; + kcdb_cred * cred = NULL; + size_t cbsize; + + if(!cbbuf) + return KHM_ERROR_INVALID_PARM; + + LockObtainRead(&l_creds); + + if(!kcdb_cred_is_active_cred(vcred)) { + code = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + cred = (kcdb_cred *) vcred; + + if(FAILED(StringCbLength(cred->name, KCDB_CRED_MAXCB_NAME, &cbsize))) { + code = KHM_ERROR_UNKNOWN; + goto _exit; + } + + cbsize += sizeof(wchar_t); + + if(!buffer || *cbbuf < cbsize) { + *cbbuf = cbsize; + code = KHM_ERROR_TOO_LONG; + goto _exit; + } + + StringCbCopy(buffer, *cbbuf, cred->name); + + *cbbuf = cbsize; + +_exit: + + LockReleaseRead(&l_creds); + return code; +} + +KHMEXP khm_int32 KHMAPI kcdb_cred_get_identity( + khm_handle vcred, + khm_handle * identity) +{ + khm_int32 code = KHM_ERROR_SUCCESS; + kcdb_cred * cred; + + if(!identity) + return KHM_ERROR_INVALID_PARM; + + LockObtainRead(&l_creds); + + if(!kcdb_cred_is_active_cred(vcred)) { + code = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + cred = (kcdb_cred *) vcred; + + kcdb_identity_hold((khm_handle) cred->identity); + + *identity = cred->identity; + +_exit: + LockReleaseRead(&l_creds); + return code; +} + +KHMEXP khm_int32 KHMAPI kcdb_cred_hold(khm_handle vcred) +{ + khm_int32 code = KHM_ERROR_SUCCESS; + kcdb_cred * cred; + + kcdb_cred_lock_write(); + + if(!kcdb_cred_is_active_cred(vcred)) { + code = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + cred = (kcdb_cred *) vcred; + + cred->refcount++; + +_exit: + kcdb_cred_unlock_write(); + return code; +} + +KHMEXP khm_int32 KHMAPI kcdb_cred_release(khm_handle vcred) +{ + khm_int32 code = KHM_ERROR_SUCCESS; + kcdb_cred * cred; + + kcdb_cred_lock_write(); + + if(!kcdb_cred_is_active_cred(vcred)) { + code = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + cred = (kcdb_cred *) vcred; + + cred->refcount--; + +_exit: + kcdb_cred_unlock_write(); + + kcdb_cred_check_and_delete(vcred); + + return code; +} + +void kcdb_cred_check_and_delete(khm_handle vcred) +{ + kcdb_cred * cred; + + LockObtainRead(&l_creds); + if(!kcdb_cred_is_cred(vcred)) { + goto _exit; + } + + cred = (kcdb_cred *) vcred; + + if(!(cred->flags & KCDB_CRED_FLAG_DELETED)) + goto _exit; + + if(cred->refcount) + goto _exit; + + LockReleaseRead(&l_creds); + kcdb_cred_lock_write(); + if(!kcdb_cred_is_cred(vcred)) { + /* did we lose the race? */ + goto _exit2; + } + + cred->magic = 0; /* no longer a cred */ + kcdb_identity_release(cred->identity); + + EnterCriticalSection(&cs_creds); + LDELETE(&kcdb_creds, cred); + LeaveCriticalSection(&cs_creds); + + kcdb_buf_delete(&cred->buf); + free(cred->name); + free(cred); + + /*TODO: notifications */ + +_exit2: + kcdb_cred_unlock_write(); + return; + +_exit: + LockReleaseRead(&l_creds); +} + +KHMEXP khm_int32 KHMAPI kcdb_cred_delete(khm_handle vcred) +{ + khm_int32 code = KHM_ERROR_SUCCESS; + kcdb_cred * cred; + + kcdb_cred_lock_write(); + + if(!kcdb_cred_is_active_cred(vcred)) { + code = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + cred = (kcdb_cred *) vcred; + + cred->flags |= KCDB_CRED_FLAG_DELETED; + +_exit: + kcdb_cred_unlock_write(); + + kcdb_cred_check_and_delete(vcred); + + return code; +} + +KHMEXP khm_int32 KHMAPI kcdb_creds_comp_attrib( + khm_handle cred1, + khm_handle cred2, + wchar_t * name) +{ + khm_int32 attr_id; + + if(KHM_FAILED(kcdb_attrib_get_id(name, &attr_id))) + return 0; + + return kcdb_creds_comp_attr(cred1, cred2, attr_id); +} + +KHMEXP khm_int32 KHMAPI kcdb_creds_comp_attr( + khm_handle vcred1, + khm_handle vcred2, + khm_int32 attr_id) +{ + khm_int32 code = 0; + kcdb_cred * cred1; + kcdb_cred * cred2; + kcdb_attrib * attrib = NULL; + kcdb_type * type = NULL; + + if(attr_id < 0 || attr_id > KCDB_ATTR_MAX_ID) + return 0; + + cred1 = (kcdb_cred *) vcred1; + cred2 = (kcdb_cred *) vcred2; + + LockObtainRead(&l_creds); + if( + !kcdb_cred_is_active_cred(vcred1) || + !kcdb_cred_is_active_cred(vcred2)) + goto _exit; + + cred1 = (kcdb_cred *) vcred1; + cred2 = (kcdb_cred *) vcred2; + + if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) + goto _exit; + + if(!(attrib->flags & KCDB_ATTR_FLAG_COMPUTED)) { + int nc = 0; + + if(!kcdb_cred_val_exist(cred1, attr_id)) { + code = -1; + nc = 1; + } + if(!kcdb_cred_val_exist(cred2, attr_id)) { + code += 1; + nc = 1; + } + + if(nc) + goto _exit; + } + + if(KHM_FAILED(kcdb_type_get_info(attrib->type, &type))) + goto _exit; + + if(attrib->flags & KCDB_ATTR_FLAG_COMPUTED) { + void * buf1 = NULL; + void * buf2 = NULL; + khm_size cb1; + khm_size cb2; + + code = 0; + + if(attrib->compute_cb(vcred1, attr_id, NULL, &cb1) != KHM_ERROR_TOO_LONG) + goto _exit_1; + + if(attrib->compute_cb(vcred2, attr_id, NULL, &cb2) != KHM_ERROR_TOO_LONG) + goto _exit_1; + + if(cb1) { + buf1 = malloc(cb1); + if(KHM_FAILED(attrib->compute_cb(vcred1, attr_id, buf1, &cb1))) + goto _exit_1; + } + if(cb2) { + buf2 = malloc(cb2); + if(KHM_FAILED(attrib->compute_cb(vcred2, attr_id, buf2, &cb2))) + goto _exit_1; + } + code = type->comp( + buf1, cb1, + buf2, cb2); +_exit_1: + if(buf1) + free(buf1); + if(buf2) + free(buf2); + + } else { + code = type->comp( + kcdb_cred_buf_get(cred1, attr_id), + kcdb_cred_buf_size(cred1, attr_id), + kcdb_cred_buf_get(cred2, attr_id), + kcdb_cred_buf_size(cred2, attr_id)); + } + +_exit: + LockReleaseRead(&l_creds); + if(attrib) + kcdb_attrib_release_info(attrib); + if(type) + kcdb_type_release_info(type); + return code; +} + +KHMEXP khm_int32 KHMAPI kcdb_creds_is_equal( + khm_handle vcred1, + khm_handle vcred2) +{ + khm_int32 code = 0; + kcdb_cred * cred1; + kcdb_cred * cred2; + + LockObtainRead(&l_creds); + if(!kcdb_cred_is_active_cred(vcred1) || + !kcdb_cred_is_active_cred(vcred2)) + goto _exit; + + if(vcred1 == vcred2) { + code = TRUE; + goto _exit; + } + + cred1 = vcred1; + cred2 = vcred2; + + if(cred1->identity == cred2->identity && + cred1->type == cred2->type && + !wcscmp(cred1->name, cred2->name)) { + code = TRUE; + } + +_exit: + LockReleaseRead(&l_creds); + return code; +} + +KHMEXP khm_int32 KHMAPI kcdb_cred_get_flags( + khm_handle vcred, + khm_int32 * pflags) +{ + khm_int32 f; + khm_int32 rv = KHM_ERROR_SUCCESS; + kcdb_cred * cred; + int release_lock = TRUE; + + if (pflags == NULL) + return KHM_ERROR_INVALID_PARM; + + LockObtainRead(&l_creds); + if (!kcdb_cred_is_active_cred(vcred)) { + *pflags = 0; + rv = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + cred = vcred; + f = cred->flags; + + /* Update flags if necessary */ + + if (!(f & KCDB_CRED_FLAG_EXPIRED) && + kcdb_cred_buf_exist(cred, KCDB_ATTR_EXPIRE)) { + + khm_int64 ftc; + + GetSystemTimeAsFileTime((LPFILETIME) &ftc); + if (ftc > *((khm_int64 *) + kcdb_cred_buf_get(cred, KCDB_ATTR_EXPIRE))) + f |= KCDB_CRED_FLAG_EXPIRED; + } + +#if 0 + /* Commented out: if the credential has expired, then checking the + renewable time is not useful */ + if (!(f & KCDB_CRED_FLAG_INVALID)) { + if (f & KCDB_CRED_FLAG_RENEWABLE) { + if (kcdb_cred_buf_exist(cred, KCDB_ATTR_RENEW_EXPIRE)) { + khm_int64 ftc; + + GetSystemTimeAsFileTime((LPFILETIME) &ftc); + if (ftc > *((khm_int64 *) kcdb_cred_buf_get(cred, KCDB_ATTR_RENEW_EXPIRE))) + f |= KCDB_CRED_FLAG_INVALID; + } + } else { + if (f & KCDB_CRED_FLAG_EXPIRED) + f |= KCDB_CRED_FLAG_INVALID; + } + } + + /* Commented out: this is a read operation. We shouldn't attempt + to lock for writing */ + if (f != cred->flags) { + LockReleaseRead(&l_creds); + LockObtainWrite(&l_creds); + /* Did we lose a race? */ + if (kcdb_cred_is_active_cred(vcred)) + cred->flags = f; + else { + rv = KHM_ERROR_INVALID_PARM; + f = 0; + } + LockReleaseWrite(&l_creds); + release_lock = FALSE; + } +#endif + + *pflags = f; + + _exit: + if (release_lock) + LockReleaseRead(&l_creds); + + return rv; +} + +KHMEXP khm_int32 KHMAPI kcdb_cred_set_flags( + khm_handle vcred, + khm_int32 flags, + khm_int32 mask) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + kcdb_cred * cred; + + LockObtainWrite(&l_creds); + if(!kcdb_cred_is_active_cred(vcred)) { + rv = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + cred = vcred; + + flags &= ~(KCDB_CRED_FLAG_DELETED); + mask &= ~(KCDB_CRED_FLAG_DELETED); + + cred->flags = + (cred->flags & (~mask)) | + (flags & mask); + + _exit: + LockReleaseWrite(&l_creds); + return rv; +} diff --git a/src/windows/identity/kcreddb/credential.h b/src/windows/identity/kcreddb/credential.h new file mode 100644 index 000000000..8104f686b --- /dev/null +++ b/src/windows/identity/kcreddb/credential.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KCDB_CREDENTIAL_H +#define __KHIMAIRA_KCDB_CREDENTIAL_H + +/* Credentials */ + +typedef struct kcdb_cred_t { + khm_int32 magic; + khm_ui_8 id; /* serial number */ + kcdb_identity * identity; + khm_int32 type; + wchar_t * name; + + khm_int32 flags; + khm_int32 refcount; + + kcdb_buf buf; + + LDCL(struct kcdb_cred_t); +} kcdb_cred; + +#define KCDB_CRED_MAGIC 0x38fb84a6 + +extern CRITICAL_SECTION cs_creds; +extern kcdb_cred * kcdb_creds; +extern RWLOCK l_creds; +extern khm_ui_8 kcdb_cred_id; + +#define kcdb_cred_val_exist(c,a) kcdb_buf_val_exist(&(c)->buf, a) +#define kcdb_cred_buf_exist(c,a) kcdb_buf_exist(&(c)->buf, a) +#define kcdb_cred_buf_get(c,a) kcdb_buf_get(&(c)->buf, a) +#define kcdb_cred_buf_size(c,a) kcdb_buf_size(&(c)->buf, a) + +#define kcdb_cred_is_cred(c) ((c) && ((kcdb_cred *) c)->magic == KCDB_CRED_MAGIC) +#define kcdb_cred_is_active_cred(c) (kcdb_cred_is_cred(c) && !(((kcdb_cred *) c)->flags & KCDB_CRED_FLAG_DELETED)) +#define kcdb_cred_lock_read() (LockObtainRead(&l_creds)) +#define kcdb_cred_unlock_read() (LockReleaseRead(&l_creds)) +#define kcdb_cred_lock_write() (LockObtainWrite(&l_creds)) +#define kcdb_cred_unlock_write() (LockReleaseWrite(&l_creds)) + +void kcdb_cred_init(void); +void kcdb_cred_exit(void); +void kcdb_cred_check_and_delete(khm_handle vcred); + +#endif diff --git a/src/windows/identity/kcreddb/credset.c b/src/windows/identity/kcreddb/credset.c new file mode 100644 index 000000000..3a39d48a6 --- /dev/null +++ b/src/windows/identity/kcreddb/credset.c @@ -0,0 +1,1132 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include + +CRITICAL_SECTION cs_credset; +kcdb_credset * kcdb_credsets = NULL; +kcdb_credset * kcdb_root_credset = NULL; + +void kcdb_credset_init(void) +{ + khm_handle rc; + + InitializeCriticalSection(&cs_credset); + kcdb_credsets = NULL; + + kcdb_credset_create(&rc); + kcdb_root_credset = (kcdb_credset *) rc; + kcdb_root_credset->flags |= KCDB_CREDSET_FLAG_ROOT; +} + +void kcdb_credset_exit(void) +{ + /*TODO: free the credsets */ + DeleteCriticalSection(&cs_credset); +} + +/* called on an unreleased credset, or with credset::cs held */ +void kcdb_credset_buf_new(kcdb_credset * cs) +{ + cs->clist = malloc(KCDB_CREDSET_INITIAL_SIZE * sizeof(kcdb_credset_credref)); + ZeroMemory(cs->clist, KCDB_CREDSET_INITIAL_SIZE * sizeof(kcdb_credset_credref)); + cs->nc_clist = KCDB_CREDSET_INITIAL_SIZE; + cs->nclist = 0; +} + +/* called on an unreleased credset, or with credset::cs held */ +void kcdb_credset_buf_delete(kcdb_credset * cs) +{ + free(cs->clist); + cs->nc_clist = 0; + cs->nclist = 0; +} + +void kcdb_credset_buf_assert_size(kcdb_credset * cs, khm_int32 nclist) +{ + if(cs->nc_clist < nclist) { + kcdb_credset_credref * new_clist; + + /* nclist had better be greater than KCDB_CREDSET_INITIAL_SIZE */ + nclist = KCDB_CREDSET_INITIAL_SIZE + + (((nclist - (KCDB_CREDSET_INITIAL_SIZE + 1)) / KCDB_CREDSET_GROWTH_FACTOR) + 1) * + KCDB_CREDSET_GROWTH_FACTOR; + + new_clist = calloc(nclist, sizeof(kcdb_credset_credref)); + + memcpy(new_clist, cs->clist, cs->nclist * sizeof(kcdb_credset_credref)); + + free(cs->clist); + + cs->clist = new_clist; + } +} + +KHMEXP khm_int32 KHMAPI kcdb_credset_create(khm_handle * result) +{ + kcdb_credset * cs; + + cs = malloc(sizeof(kcdb_credset)); + ZeroMemory(cs, sizeof(kcdb_credset)); + + cs->magic = KCDB_CREDSET_MAGIC; + InitializeCriticalSection(&(cs->cs)); + LINIT(cs); + kcdb_credset_buf_new(cs); + cs->version = 0; + cs->seal_count = 0; + + EnterCriticalSection(&cs_credset); + LPUSH(&kcdb_credsets, cs); + LeaveCriticalSection(&cs_credset); + + *result = (khm_handle) cs; + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kcdb_credset_delete(khm_handle vcredset) +{ + kcdb_credset * cs; + int i; + + if(!kcdb_credset_is_credset(vcredset)) { + return KHM_ERROR_INVALID_PARM; + } + + cs = (kcdb_credset *) vcredset; + + EnterCriticalSection(&cs_credset); + LDELETE(&kcdb_credsets, cs); + LeaveCriticalSection(&cs_credset); + + EnterCriticalSection(&(cs->cs)); + cs->magic = 0; + + for(i=0;inclist;i++) { + if(cs->clist[i].cred) { + kcdb_cred_release((khm_handle) cs->clist[i].cred); + } + } + kcdb_credset_buf_delete(cs); + + LeaveCriticalSection(&(cs->cs)); + DeleteCriticalSection(&(cs->cs)); + + free(cs); + + return KHM_ERROR_SUCCESS; +} + +/*! \internal + +Collect credentials from cs2 to cs1 which have already been selected into +cl1 and cl2. + +- Credentials in cl2 that are not in cl1 will get added to cs1 +- Credentials in cl1 that are not in cl2 will get removed from cs1 +- Credentials in cl1 and cl2 will be updated in cs1 + +cl1 and cl2 will be modified. +*/ +khm_int32 kcdb_credset_collect_core( + kcdb_credset * cs1, + kcdb_cred ** cl1, + khm_int32 ncl1, + kcdb_credset * cs2, + kcdb_cred ** cl2, + khm_int32 ncl2, + khm_int32 * delta) +{ + int i, j; + int ldelta = 0; + khm_int32 rv; + + /* find matching creds and update them */ + for(i=0; ics)); + EnterCriticalSection(&(rcs->cs)); + + /* enumerate through the root and given credential sets and select + the ones we want */ + + if(rcs->nclist > 0) + r_sel = malloc(sizeof(kcdb_cred *) * rcs->nclist); + if(cs->nclist > 0) + c_sel = malloc(sizeof(kcdb_cred *) * cs->nclist); + nr_sel = 0; + nc_sel = 0; + + for(i=0; inclist; i++) { + if(rcs->clist[i].cred && + (!identity || rcs->clist[i].cred->identity == identity) && + (type==KCDB_CREDTYPE_ALL || rcs->clist[i].cred->type == type)) + { + r_sel[nr_sel++] = rcs->clist[i].cred; + } + } + + for(i=0; inclist; i++) { + if(cs->clist[i].cred && + (!identity || cs->clist[i].cred->identity == identity) && + (type==KCDB_CREDTYPE_ALL || cs->clist[i].cred->type == type)) + { + c_sel[nc_sel++] = cs->clist[i].cred; + } + } + + rcs->version++; + + code = kcdb_credset_collect_core( + rcs, + r_sel, + nr_sel, + cs, + c_sel, + nc_sel, + delta); + + LeaveCriticalSection(&(rcs->cs)); + LeaveCriticalSection(&(cs->cs)); + + if(r_sel) + free(r_sel); + if(c_sel) + free(c_sel); + + return code; +} + +KHMEXP khm_int32 KHMAPI kcdb_credset_collect_filtered( + khm_handle cs_dest, + khm_handle cs_src, + kcdb_cred_filter_func filter, + void * rock, + khm_int32 * delta) +{ + kcdb_credset * cs; + kcdb_credset * rcs; + khm_int32 code = KHM_ERROR_SUCCESS; + kcdb_cred ** r_sel = NULL; + kcdb_cred ** c_sel = NULL; + int nr_sel, nc_sel; + int i; + khm_int32 cs_f = 0; + khm_int32 rcs_f = 0; + + if((cs_src && !kcdb_credset_is_credset(cs_src)) || + (cs_dest && !kcdb_credset_is_credset(cs_dest)) || + (cs_src == cs_dest)) /* works because credsets use shared + handles */ + return KHM_ERROR_INVALID_PARM; + + if(cs_src) + cs = (kcdb_credset *) cs_src; + else { + cs = kcdb_root_credset; + cs_f = KCDB_CREDCOLL_FILTER_ROOT; + } + + if(cs_dest) + rcs = (kcdb_credset *) cs_dest; + else { + rcs = kcdb_root_credset; + rcs_f = KCDB_CREDCOLL_FILTER_ROOT; + } + + if (kcdb_credset_is_sealed(rcs)) + return KHM_ERROR_INVALID_OPERATION; + + EnterCriticalSection(&(cs->cs)); + EnterCriticalSection(&(rcs->cs)); + +#ifdef DEBUG + assert(!(rcs->flags & KCDB_CREDSET_FLAG_ENUM)); + assert(!(cs->flags & KCDB_CREDSET_FLAG_ENUM)); +#endif + + if(rcs->nclist) + r_sel = malloc(sizeof(kcdb_cred *) * rcs->nclist); + if(cs->nclist) + c_sel = malloc(sizeof(kcdb_cred *) * cs->nclist); + nr_sel = 0; + nc_sel = 0; + + rcs->flags |= KCDB_CREDSET_FLAG_ENUM; + + for(i=0; inclist; i++) { + if(rcs->clist[i].cred && + (*filter)((khm_handle)rcs->clist[i].cred, + KCDB_CREDCOLL_FILTER_DEST | rcs_f, + rock)) + { + r_sel[nr_sel++] = rcs->clist[i].cred; + } + } + + rcs->flags &= ~KCDB_CREDSET_FLAG_ENUM; + cs->flags |= KCDB_CREDSET_FLAG_ENUM; + + for(i=0; inclist; i++) { + if(cs->clist[i].cred && filter((khm_handle)rcs->clist[i].cred, KCDB_CREDCOLL_FILTER_SRC | cs_f, rock)) + { + c_sel[nc_sel++] = cs->clist[i].cred; + } + } + + cs->flags &= ~KCDB_CREDSET_FLAG_ENUM; + + rcs->version++; + + code = kcdb_credset_collect_core( + rcs, + r_sel, + nr_sel, + cs, + c_sel, + nc_sel, + delta); + + LeaveCriticalSection(&(rcs->cs)); + LeaveCriticalSection(&(cs->cs)); + + if(r_sel) + free(r_sel); + if(c_sel) + free(c_sel); + + return code; +} + +KHMEXP khm_int32 KHMAPI kcdb_credset_flush(khm_handle vcredset) +{ + int i; + kcdb_credset * cs; + + if(!kcdb_credset_is_credset(vcredset)) + return KHM_ERROR_INVALID_PARM; + + cs = (kcdb_credset *) vcredset; + + if (kcdb_credset_is_sealed(cs)) + return KHM_ERROR_INVALID_OPERATION; + + EnterCriticalSection(&(cs->cs)); + +#ifdef DEBUG + assert(!(cs->flags & KCDB_CREDSET_FLAG_ENUM)); +#endif + + for(i=0;inclist;i++) { + if(cs->clist[i].cred) { + kcdb_cred_release((khm_handle) cs->clist[i].cred); + } + } + cs->nclist = 0; + LeaveCriticalSection(&(cs->cs)); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kcdb_credset_extract( + khm_handle destcredset, + khm_handle sourcecredset, + khm_handle identity, + khm_int32 type) +{ + khm_int32 code = KHM_ERROR_SUCCESS; + kcdb_credset * dest; + kcdb_credset * src; + int isRoot = 0; + khm_size srcSize = 0; + int i; + + if(!kcdb_credset_is_credset(destcredset)) + return KHM_ERROR_INVALID_PARM; + + if(sourcecredset) { + if(!kcdb_credset_is_credset(sourcecredset)) + return KHM_ERROR_INVALID_PARM; + } else { + sourcecredset = kcdb_root_credset; + } + + if (sourcecredset == kcdb_root_credset) + isRoot = 1; + + src = (kcdb_credset *) sourcecredset; + dest = (kcdb_credset *) destcredset; + + if (kcdb_credset_is_sealed(dest)) + return KHM_ERROR_INVALID_OPERATION; + + EnterCriticalSection(&(src->cs)); + EnterCriticalSection(&(dest->cs)); + +#ifdef DEBUG + assert(!(dest->flags & KCDB_CREDSET_FLAG_ENUM)); +#endif + + if(KHM_FAILED(kcdb_credset_get_size(sourcecredset, &srcSize))) { + code = KHM_ERROR_UNKNOWN; + goto _exit; + } + + kcdb_cred_lock_read(); + + for(i=0; i < (int) srcSize; i++) { + kcdb_cred * c; + + c = src->clist[i].cred; + if(kcdb_cred_is_active_cred((khm_handle) c) && + (!identity || c->identity == identity) && + (type==KCDB_TYPE_INVALID || c->type == type)) + { + if(isRoot) { + khm_handle newcred; + + kcdb_cred_unlock_read(); + kcdb_cred_dup((khm_handle) c, &newcred); + kcdb_credset_add_cred(destcredset, newcred, -1); + kcdb_cred_release(newcred); + kcdb_cred_lock_read(); + } else { + kcdb_cred_unlock_read(); + kcdb_credset_add_cred(destcredset, (khm_handle) c, -1); + kcdb_cred_lock_read(); + } + } + } + + kcdb_cred_unlock_read(); + +_exit: + LeaveCriticalSection(&(dest->cs)); + LeaveCriticalSection(&(src->cs)); + + return code; +} + +KHMEXP khm_int32 KHMAPI kcdb_credset_extract_filtered( + khm_handle destcredset, + khm_handle sourcecredset, + kcdb_cred_filter_func filter, + void * rock) +{ + khm_int32 code = KHM_ERROR_SUCCESS; + kcdb_credset * dest; + kcdb_credset * src; + int isRoot = 0; + khm_size srcSize = 0; + int i; + + if(!kcdb_credset_is_credset(destcredset)) + return KHM_ERROR_INVALID_PARM; + + if(sourcecredset) { + if(!kcdb_credset_is_credset(sourcecredset)) + return KHM_ERROR_INVALID_PARM; + } else { + sourcecredset = kcdb_root_credset; + isRoot = 1; + } + + src = (kcdb_credset *) sourcecredset; + dest = (kcdb_credset *) destcredset; + + if (kcdb_credset_is_sealed(dest)) + return KHM_ERROR_INVALID_OPERATION; + + EnterCriticalSection(&(src->cs)); + EnterCriticalSection(&(dest->cs)); + +#ifdef DEBUG + assert(!(dest->flags & KCDB_CREDSET_FLAG_ENUM)); +#endif + + if(KHM_FAILED(kcdb_credset_get_size(sourcecredset, &srcSize))) { + code = KHM_ERROR_UNKNOWN; + goto _exit; + } + + kcdb_cred_lock_read(); + + dest->flags |= KCDB_CREDSET_FLAG_ENUM; + + for(i=0; i < (int) srcSize; i++) { + kcdb_cred * c; + + c = src->clist[i].cred; + if(kcdb_cred_is_active_cred((khm_handle) c) && + filter(c, 0, rock)) + { + if(isRoot) { + khm_handle newcred; + + kcdb_cred_unlock_read(); + kcdb_cred_dup((khm_handle) c, &newcred); + kcdb_credset_add_cred(destcredset, newcred, -1); + kcdb_cred_release(newcred); + kcdb_cred_lock_read(); + } else { + kcdb_cred_unlock_read(); + kcdb_credset_add_cred(destcredset, (khm_handle) c, -1); + kcdb_cred_lock_read(); + } + } + } + + dest->flags &= ~KCDB_CREDSET_FLAG_ENUM; + + kcdb_cred_unlock_read(); + +_exit: + LeaveCriticalSection(&(dest->cs)); + LeaveCriticalSection(&(src->cs)); + + return code; +} + +KHMEXP khm_int32 KHMAPI kcdb_credset_apply(khm_handle vcredset, kcdb_cred_apply_func f, void * rock) +{ + kcdb_credset * cs; + khm_int32 rv = KHM_ERROR_SUCCESS; + int i; + + if(vcredset != NULL && !kcdb_credset_is_credset(vcredset)) + return KHM_ERROR_INVALID_PARM; + + if(vcredset == NULL) { + cs = kcdb_root_credset; + } else { + cs = (kcdb_credset *) vcredset; + } + + EnterCriticalSection(&cs->cs); + +#ifdef DEBUG + assert(!(cs->flags & KCDB_CREDSET_FLAG_ENUM)); +#endif + + cs->flags |= KCDB_CREDSET_FLAG_ENUM; + + for(i=0; inclist; i++) { + if(!kcdb_cred_is_active_cred(cs->clist[i].cred)) + continue; + + if(KHM_FAILED(f((khm_handle) cs->clist[i].cred, rock))) + break; + } + + cs->flags &= ~KCDB_CREDSET_FLAG_ENUM; + + LeaveCriticalSection(&cs->cs); + + if(inclist) + rv = KHM_ERROR_EXIT; + + return rv; +} + +KHMEXP khm_int32 KHMAPI kcdb_credset_get_cred( + khm_handle vcredset, + khm_int32 idx, + khm_handle * cred) +{ + kcdb_credset * cs; + khm_int32 code = KHM_ERROR_SUCCESS; + + if(!kcdb_credset_is_credset(vcredset)) + return KHM_ERROR_INVALID_PARM; + + cs = (kcdb_credset *) vcredset; + + *cred = NULL; + + EnterCriticalSection(&(cs->cs)); + if(idx < 0 || idx >= cs->nclist) + code = KHM_ERROR_OUT_OF_BOUNDS; + else if(!cs->clist[idx].cred || !kcdb_cred_is_active_cred((khm_handle) cs->clist[idx].cred)) { + code = KHM_ERROR_DELETED; + if(cs->clist[idx].cred) { + kcdb_cred_release((khm_handle) cs->clist[idx].cred); + cs->clist[idx].cred = NULL; + } + } + else { + kcdb_cred_hold((khm_handle) cs->clist[idx].cred); + *cred = cs->clist[idx].cred; + } + LeaveCriticalSection(&(cs->cs)); + return code; +} + +KHMEXP khm_int32 KHMAPI kcdb_credset_find_filtered( + khm_handle credset, + khm_int32 idx_start, + kcdb_cred_filter_func f, + void * rock, + khm_handle * cred, + khm_int32 * idx) +{ + kcdb_credset * cs; + khm_int32 rv = KHM_ERROR_SUCCESS; + int i; + + if((credset && !kcdb_credset_is_credset(credset)) || + (!f || !cred)) + return KHM_ERROR_INVALID_PARM; + + if(credset) + cs = (kcdb_credset *) credset; + else + cs = kcdb_root_credset; + + EnterCriticalSection(&cs->cs); + + if(idx_start < 0) + i = 0; + else + i = idx_start + 1; + +#ifdef DEBUG + assert(!(cs->flags & KCDB_CREDSET_FLAG_ENUM)); +#endif + + cs->flags |= KCDB_CREDSET_FLAG_ENUM; + + for(; i < cs->nclist; i++) { + if(kcdb_cred_is_active_cred(cs->clist[i].cred) && + (*f)((khm_handle) cs->clist[i].cred, 0, rock) != 0) + break; + } + + cs->flags &= ~KCDB_CREDSET_FLAG_ENUM; + + if(i < cs->nclist) { + *cred = (khm_handle) cs->clist[i].cred; + kcdb_cred_hold(*cred); + if(idx) + *idx = i; + } else { + rv = KHM_ERROR_NOT_FOUND; + } + + LeaveCriticalSection(&cs->cs); + + return rv; +} + +KHMEXP khm_int32 KHMAPI +kcdb_credset_find_cred(khm_handle vcredset, + khm_handle vcred_src, + khm_handle *cred_dest) { + kcdb_credset * cs; + khm_handle cred = NULL; + int idx; + + if (!kcdb_credset_is_credset(vcredset)) + return KHM_ERROR_INVALID_PARM; + + if (!kcdb_cred_is_active_cred(vcred_src)) + return KHM_ERROR_INVALID_PARM; + + cs = (kcdb_credset *) vcredset; + + EnterCriticalSection(&cs->cs); + for (idx = 0; idx < cs->nclist; idx++) { + if (cs->clist[idx].cred && + kcdb_creds_is_equal(vcred_src, cs->clist[idx].cred)) { + cred = cs->clist[idx].cred; + break; + } + } + + if (cred) + kcdb_cred_hold(cred); + + LeaveCriticalSection(&cs->cs); + + if (cred) { + if (cred_dest) + *cred_dest = cred; + else + kcdb_cred_release(cred); + + return KHM_ERROR_SUCCESS; + } else { + return KHM_ERROR_NOT_FOUND; + } +} + +KHMEXP khm_int32 KHMAPI kcdb_credset_del_cred( + khm_handle vcredset, + khm_int32 idx) +{ + kcdb_credset * cs; + khm_int32 code = KHM_ERROR_SUCCESS; + + if(!kcdb_credset_is_credset(vcredset)) + return KHM_ERROR_INVALID_PARM; + + cs = (kcdb_credset *) vcredset; + + if (kcdb_credset_is_sealed(cs)) + return KHM_ERROR_INVALID_OPERATION; + + EnterCriticalSection(&(cs->cs)); + if(idx < 0 || idx >= cs->nclist) { + code = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + if(cs->clist[idx].cred) + kcdb_cred_release((khm_handle) cs->clist[idx].cred); + + if (!(cs->flags & KCDB_CREDSET_FLAG_ENUM)) { + + if(idx + 1 < cs->nclist) + memmove(&(cs->clist[idx]), + &(cs->clist[idx+1]), + sizeof(kcdb_credset_credref) * + (cs->nclist - (idx + 1))); + + cs->nclist--; + } else { + cs->clist[idx].cred = NULL; + } + +_exit: + LeaveCriticalSection(&(cs->cs)); + + return code; +} + +khm_int32 kcdb_credset_update_cred_ref( + khm_handle credset, + khm_handle cred) +{ + kcdb_credset * cs; + khm_int32 code = KHM_ERROR_SUCCESS; + int i; + + if(!kcdb_credset_is_credset(credset)) + return KHM_ERROR_INVALID_PARM; + + cs = (kcdb_credset *) credset; + + EnterCriticalSection(&(cs->cs)); + + for(i=0; inclist; i++) { + if(cs->clist[i].cred == cred) + break; + } + + if(inclist) { + cs->clist[i].version = cs->version; + } else { + code = KHM_ERROR_NOT_FOUND; + } + + LeaveCriticalSection(&(cs->cs)); + return code; +} + +KHMEXP khm_int32 KHMAPI kcdb_credset_del_cred_ref( + khm_handle credset, + khm_handle cred) +{ + kcdb_credset * cs; + khm_int32 code = KHM_ERROR_SUCCESS; + int i; + + if(!kcdb_credset_is_credset(credset)) + return KHM_ERROR_INVALID_PARM; + + cs = (kcdb_credset *) credset; + + if (kcdb_credset_is_sealed(cs)) + return KHM_ERROR_INVALID_OPERATION; + + EnterCriticalSection(&(cs->cs)); + + for(i=0; inclist; i++) { + if(cs->clist[i].cred == cred) + break; + } + + if(inclist) { + code = kcdb_credset_del_cred(credset, i); + } else { + code = KHM_ERROR_NOT_FOUND; + } + + LeaveCriticalSection(&(cs->cs)); + return code; +} + +KHMEXP khm_int32 KHMAPI kcdb_credset_add_cred( + khm_handle credset, + khm_handle cred, + khm_int32 idx) +{ + int new_idx; + kcdb_credset * cs; + khm_int32 code = KHM_ERROR_SUCCESS; + + if(!kcdb_credset_is_credset(credset)) + return KHM_ERROR_INVALID_PARM; + + cs = (kcdb_credset *) credset; + + if (kcdb_credset_is_sealed(cs)) + return KHM_ERROR_INVALID_OPERATION; + + EnterCriticalSection(&(cs->cs)); + + kcdb_credset_buf_assert_size(cs, cs->nclist + 1); + + if(idx < 0 || idx > cs->nclist) + new_idx = cs->nclist; + else if(idx < cs->nclist){ +#ifdef DEBUG + assert(!(cs->flags & KCDB_CREDSET_FLAG_ENUM)); +#endif + memmove(&(cs->clist[idx+1]), &(cs->clist[idx]), (cs->nclist - idx)*sizeof(cs->clist[0])); + new_idx = idx; + } else + new_idx = idx; + + kcdb_cred_hold(cred); + + cs->clist[new_idx].cred = (kcdb_cred *) cred; + cs->clist[new_idx].version = cs->version; + cs->nclist++; + + LeaveCriticalSection(&(cs->cs)); + return code; +} + +KHMEXP khm_int32 KHMAPI kcdb_credset_get_size( + khm_handle credset, + khm_size * size) +{ + kcdb_credset * cs; + + *size = 0; + + /* we don't rely on this working, since we can't purge a sealed + credset, although we can measure its size. */ + kcdb_credset_purge(credset); + + if (credset == NULL) + cs = kcdb_root_credset; + else + cs = (kcdb_credset *) credset; + + EnterCriticalSection(&(cs->cs)); + /* while it may seem a bit redundant to get a lock, it ensures that + that the size that we return is consistent with the current state + of the credential set */ + *size = cs->nclist; + LeaveCriticalSection(&(cs->cs)); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kcdb_credset_purge(khm_handle credset) +{ + khm_int32 code = KHM_ERROR_SUCCESS; + kcdb_credset * cs; + int i,j; + + if(!kcdb_credset_is_credset(credset)) + return KHM_ERROR_INVALID_PARM; + + cs = (kcdb_credset *) credset; + + if (kcdb_credset_is_sealed(cs)) + return KHM_ERROR_INVALID_OPERATION; + + EnterCriticalSection(&(cs->cs)); + + /* we can't purge a credset while an enumeration operation is in + progress. */ + if (cs->flags & KCDB_CREDSET_FLAG_ENUM) { + code = KHM_ERROR_INVALID_OPERATION; + goto _exit; + } + + for(i=0,j=0; i < cs->nclist; i++) { + if(cs->clist[i].cred) { + if(!kcdb_cred_is_active_cred((khm_handle) cs->clist[i].cred)) { + kcdb_cred_release((khm_handle) cs->clist[i].cred); + } else if(i != j) { + cs->clist[j++] = cs->clist[i]; + } else + j++; + } + } + cs->nclist = j; + + _exit: + LeaveCriticalSection(&(cs->cs)); + return code; +} + +KHMEXP khm_int32 KHMAPI +kcdb_credset_seal(khm_handle credset) { + kcdb_credset * cs; + + if (!kcdb_credset_is_credset(credset)) + return KHM_ERROR_INVALID_PARM; + + cs = (kcdb_credset *) credset; + + EnterCriticalSection(&cs->cs); + cs->seal_count++; + LeaveCriticalSection(&cs->cs); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +kcdb_credset_unseal(khm_handle credset) { + kcdb_credset * cs; + khm_int32 rv; + + if (!kcdb_credset_is_credset(credset)) + return KHM_ERROR_INVALID_PARM; + + cs = (kcdb_credset *) credset; + + EnterCriticalSection(&cs->cs); + if (cs->seal_count > 0) { + cs->seal_count--; + rv = KHM_ERROR_SUCCESS; + } else { + rv = KHM_ERROR_INVALID_OPERATION; + } + LeaveCriticalSection(&cs->cs); + + return rv; +} + + +/* wrapper for qsort and also parameter gobbling FSM. */ +int __cdecl kcdb_creds_comp_wrapper(const void * a, const void * b) +{ + static void * rock = NULL; + static kcdb_cred_comp_func comp = NULL; + + if(!b) { + rock = (void *) a; + return 0; + } + + if(!a) { + comp = (kcdb_cred_comp_func) b; + return 0; + } + + return comp((khm_handle) ((kcdb_credset_credref *)a)->cred, (khm_handle) ((kcdb_credset_credref *)b)->cred, rock); +} + +KHMEXP khm_int32 KHMAPI kcdb_credset_sort( + khm_handle credset, + kcdb_cred_comp_func comp, + void * rock) +{ + khm_int32 code = KHM_ERROR_SUCCESS; + kcdb_credset * cs; + + if(!kcdb_credset_is_credset(credset)) + return KHM_ERROR_INVALID_PARM; + + cs = (kcdb_credset *) credset; + + if (kcdb_credset_is_sealed(cs)) + return KHM_ERROR_INVALID_OPERATION; + + EnterCriticalSection(&(cs->cs)); + +#ifdef DEBUG + assert(!(cs->flags & KCDB_CREDSET_FLAG_ENUM)); +#endif + + kcdb_creds_comp_wrapper(rock, NULL); + kcdb_creds_comp_wrapper(NULL, (void *) comp); + + qsort(cs->clist, cs->nclist, sizeof(kcdb_credset_credref), kcdb_creds_comp_wrapper); + + LeaveCriticalSection(&(cs->cs)); + return code; +} + +KHMEXP khm_int32 KHMAPI kcdb_cred_comp_generic( + khm_handle cred1, + khm_handle cred2, + void * rock) +{ + kcdb_cred_comp_order * o = (kcdb_cred_comp_order *) rock; + int i; + khm_int32 r = 0; + khm_int32 f1, f2; + khm_int32 pt; + + for(i=0; inFields; i++) { + if (o->fields[i].order & KCDB_CRED_COMP_INITIAL_FIRST) { + + kcdb_cred_get_flags(cred1, &f1); + kcdb_cred_get_flags(cred2, &f2); + + if (((f1 ^ f2) & KCDB_CRED_FLAG_INITIAL) == 0) { + kcdb_cred_get_type(cred1, &f1); + kcdb_cred_get_type(cred2, &f2); + kcdb_identity_get_type(&pt); + + if (f1 == f2) + r = 0; + else if (f1 == pt) + r = -1; + else if (f2 == pt) + r = 1; + else + r = 0; + } else if (f1 & KCDB_CRED_FLAG_INITIAL) + r = -1; + else + r = 1; + } else { + r = 0; + } + + if (r == 0) + r = kcdb_creds_comp_attr(cred1,cred2,o->fields[i].attrib); + + if(r != 0) { + if(o->fields[i].order & KCDB_CRED_COMP_DECREASING) + r = -r; + break; + } + } + + return r; +} diff --git a/src/windows/identity/kcreddb/credset.h b/src/windows/identity/kcreddb/credset.h new file mode 100644 index 000000000..c19246540 --- /dev/null +++ b/src/windows/identity/kcreddb/credset.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KCDB_CREDSET_H +#define __KHIMAIRA_KCDB_CREDSET_H + +/* credset */ + +typedef struct kcdb_credset_credref_t { + khm_int32 version; + kcdb_cred * cred; +} kcdb_credset_credref; + +typedef struct kcdb_credset_t { + khm_int32 magic; + khm_int32 flags; + CRITICAL_SECTION cs; + + kcdb_credset_credref * clist; + khm_int32 nc_clist; /* total capacity */ + khm_int32 nclist; /* current load */ + + khm_int32 version; /* data version */ + + khm_int32 seal_count; /* number of seals applied to the + credset */ + + struct kcdb_credset_t * next; + struct kcdb_credset_t * prev; +} kcdb_credset; + +#define KCDB_CREDSET_MAGIC 0x63a84f8b + +#define KCDB_CREDSET_FLAG_ROOT 1 + +/* the credset is in the process of being enumerated */ +#define KCDB_CREDSET_FLAG_ENUM 2 + +#define kcdb_credset_is_credset(c) ((c) && ((kcdb_credset *)c)->magic == KCDB_CREDSET_MAGIC) + +#define kcdb_credset_is_sealed(c) ((c)->seal_count != 0) + +#define KCDB_CREDSET_INITIAL_SIZE 1024 +#define KCDB_CREDSET_GROWTH_FACTOR 1024 + +void kcdb_credset_init(void); +void kcdb_credset_exit(void); +khm_int32 kcdb_credset_update_cred_ref( + khm_handle credset, + khm_handle cred); + +#endif diff --git a/src/windows/identity/kcreddb/credtype.c b/src/windows/identity/kcreddb/credtype.c new file mode 100644 index 000000000..dc2b7b85a --- /dev/null +++ b/src/windows/identity/kcreddb/credtype.c @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include + +CRITICAL_SECTION cs_credtype; +kcdb_credtype_i ** kcdb_credtype_tbl = NULL; +kcdb_credtype_i * kcdb_credtypes = NULL; + +void kcdb_credtype_init(void) +{ + InitializeCriticalSection(&cs_credtype); + kcdb_credtypes = NULL; + kcdb_credtype_tbl = malloc(sizeof(kcdb_credtype_i *) * (KCDB_CREDTYPE_MAX_ID+1)); + ZeroMemory(kcdb_credtype_tbl, sizeof(kcdb_credtype_i *) * (KCDB_CREDTYPE_MAX_ID+1)); +} + +void kcdb_credtype_exit(void) +{ + /*TODO:Free up the cred types */ + free(kcdb_credtype_tbl); + DeleteCriticalSection(&cs_credtype); +} + +/* Called with cs_credtype held */ +void kcdb_credtype_check_and_delete(khm_int32 id) +{ + kcdb_credtype_i * ict; + ict = kcdb_credtype_tbl[id]; + if(!ict) + return; + + if((ict->flags & KCDB_CTI_FLAG_DELETED) && + !ict->refcount) + { + kcdb_credtype_tbl[id] = NULL; + LDELETE(&kcdb_credtypes, ict); + + free(ict->ct.name); + if(ict->ct.short_desc) + free(ict->ct.short_desc); + if(ict->ct.long_desc) + free(ict->ct.long_desc); + if(ict->ct.sub) + kmq_delete_subscription(ict->ct.sub); + + free(ict); + } +} + +KHMEXP khm_int32 KHMAPI kcdb_credtype_register(kcdb_credtype * type, khm_int32 * new_id) +{ + khm_int32 id; + kcdb_credtype_i * ict; + size_t cb_name; + size_t cb_short_desc; + size_t cb_long_desc; + int i; + + if(!type) + return KHM_ERROR_INVALID_PARM; + + if(type->id >= KCDB_CREDTYPE_MAX_ID) + return KHM_ERROR_INVALID_PARM; + + if(type->name) { + if(FAILED(StringCbLength(type->name, KCDB_MAXCB_NAME, &cb_name))) + return KHM_ERROR_TOO_LONG; + cb_name += sizeof(wchar_t); + } else + return KHM_ERROR_INVALID_PARM; + + if(type->short_desc) { + if(FAILED(StringCbLength(type->short_desc, KCDB_MAXCB_SHORT_DESC, &cb_short_desc))) + return KHM_ERROR_TOO_LONG; + cb_short_desc += sizeof(wchar_t); + } else + cb_short_desc = 0; + + if(type->long_desc) { + if(FAILED(StringCbLength(type->long_desc, KCDB_MAXCB_LONG_DESC, &cb_long_desc))) + return KHM_ERROR_TOO_LONG; + cb_long_desc += sizeof(wchar_t); + } else + cb_long_desc = 0; + + if(type->sub == NULL) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_credtype); + + if(type->id < 0) { + if(KHM_FAILED(kcdb_credtype_get_next_free_id(&id))) { + LeaveCriticalSection(&cs_credtype); + return KHM_ERROR_NO_RESOURCES; + } + } + else + id = type->id; + + if(kcdb_credtype_tbl[id]) { + LeaveCriticalSection(&cs_credtype); + return KHM_ERROR_DUPLICATE; + } + + for(i=0;i<=KCDB_CREDTYPE_MAX_ID;i++) { + if(kcdb_credtype_tbl[i] && !wcscmp(kcdb_credtype_tbl[i]->ct.name, type->name)) { + LeaveCriticalSection(&cs_credtype); + return KHM_ERROR_DUPLICATE; + } + } + + ict = malloc(sizeof(kcdb_credtype_i)); + ZeroMemory(ict, sizeof(kcdb_credtype_i)); + + ict->ct.name = malloc(cb_name); + StringCbCopy(ict->ct.name, cb_name, type->name); + + if(cb_short_desc) { + ict->ct.short_desc = malloc(cb_short_desc); + StringCbCopy(ict->ct.short_desc, cb_short_desc, type->short_desc); + } + + if(cb_long_desc) { + ict->ct.long_desc = malloc(cb_long_desc); + StringCbCopy(ict->ct.long_desc, cb_long_desc, type->long_desc); + } + + ict->ct.id = id; + + ict->ct.icon = type->icon; + + ict->ct.sub = type->sub; + + kcdb_credtype_tbl[id] = ict; + + LPUSH(&kcdb_credtypes, ict); + + LeaveCriticalSection(&cs_credtype); + + kcdb_credtype_post_message(KCDB_OP_INSERT, &(ict->ct)); + + if (new_id) + *new_id = id; + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kcdb_credtype_get_info( + khm_int32 id, + kcdb_credtype ** type) +{ + int found = 0; + + if(id < 0 || id > KCDB_CREDTYPE_MAX_ID) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_credtype); + if(kcdb_credtype_tbl[id] && + !(kcdb_credtype_tbl[id]->flags & KCDB_CTI_FLAG_DELETED)) + { + found = 1; + if(type) { + *type = &(kcdb_credtype_tbl[id]->ct); + kcdb_credtype_hold(kcdb_credtype_tbl[id]); + } + } else { + if(type) + *type = NULL; + } + LeaveCriticalSection(&cs_credtype); + + if(found) + return KHM_ERROR_SUCCESS; + else + return KHM_ERROR_NOT_FOUND; +} + +KHMEXP khm_int32 KHMAPI kcdb_credtype_release_info(kcdb_credtype * type) +{ + kcdb_credtype_i * ict; + + if(!type) + return KHM_ERROR_INVALID_PARM; + + ict = (kcdb_credtype_i *) type; + return kcdb_credtype_release(ict); +} + +KHMEXP khm_int32 KHMAPI kcdb_credtype_unregister(khm_int32 id) +{ + kcdb_credtype_i * ict; + + if(id < 0 || id > KCDB_CREDTYPE_MAX_ID) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_credtype); + ict = kcdb_credtype_tbl[id]; + ict->flags |= KCDB_CTI_FLAG_DELETED; + kcdb_credtype_check_and_delete(id); + LeaveCriticalSection(&cs_credtype); + + //kcdb_credtype_post_message(KCDB_OP_DELETE, &(ict->ct)); + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_handle KHMAPI kcdb_credtype_get_sub(khm_int32 id) +{ + kcdb_credtype_i * t; + khm_handle s; + + if(id < 0 || id > KCDB_CREDTYPE_MAX_ID) + return NULL; + + EnterCriticalSection(&cs_credtype); + t = kcdb_credtype_tbl[id]; + if(t) + s = t->ct.sub; + else + s = NULL; + LeaveCriticalSection(&cs_credtype); + + return s; +} + +KHMEXP khm_int32 KHMAPI kcdb_credtype_describe( + khm_int32 id, + wchar_t * buf, + khm_size * cbbuf, + khm_int32 flags) +{ + size_t s; + size_t maxs; + wchar_t * str; + kcdb_credtype_i * t; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(!cbbuf || id < 0 || id > KCDB_CREDTYPE_MAX_ID) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_credtype); + t = kcdb_credtype_tbl[id]; + if(t) { + if(flags & KCDB_TS_SHORT) { + str = (t->ct.short_desc)?t->ct.short_desc:t->ct.name; + maxs = (t->ct.short_desc)?KCDB_MAXCB_SHORT_DESC:KCDB_MAXCB_NAME; + } else { + str = (t->ct.long_desc)?t->ct.long_desc:((t->ct.short_desc)?t->ct.short_desc:t->ct.name); + maxs = (t->ct.long_desc)?KCDB_MAXCB_LONG_DESC:((t->ct.short_desc)?KCDB_MAXCB_SHORT_DESC:KCDB_MAXCB_NAME); + } + StringCbLength(str, maxs, &s); + s += sizeof(wchar_t); + if(!buf || *cbbuf < s) { + *cbbuf = s; + rv = KHM_ERROR_TOO_LONG; + } else { +#pragma warning(push) +#pragma warning(disable:4995) + wcscpy(buf, str); /* str is one of the string fields in t->ct which has + been validated when the type was registered. */ +#pragma warning(pop) + *cbbuf = s; + } + } else { + if(buf && *cbbuf > 0) + *buf = L'\0'; + *cbbuf = 0; + rv = KHM_ERROR_NOT_FOUND; + } + LeaveCriticalSection(&cs_credtype); + + return rv; +} + + +KHMEXP khm_int32 KHMAPI kcdb_credtype_get_name( + khm_int32 id, + wchar_t * buf, + khm_size * cbbuf) +{ + size_t s; + kcdb_credtype_i * t; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(!cbbuf || id < 0 || id > KCDB_CREDTYPE_MAX_ID) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_credtype); + t = kcdb_credtype_tbl[id]; + if(t) { + StringCbLength(t->ct.name, KCDB_MAXCB_NAME, &s); + s += sizeof(wchar_t); + if(!buf || *cbbuf < s) { + *cbbuf = s; + rv = KHM_ERROR_TOO_LONG; + } else { +#pragma warning(push) +#pragma warning(disable: 4995) + wcscpy(buf, t->ct.name); /* t->ct.name was validated when the type was registered */ +#pragma warning(pop) + *cbbuf = s; + } + } else { + *cbbuf = 0; + rv = KHM_ERROR_NOT_FOUND; + } + LeaveCriticalSection(&cs_credtype); + + return rv; +} + +KHMEXP khm_int32 KHMAPI kcdb_credtype_get_id( + wchar_t * name, + khm_int32 * id) +{ + int i; + + *id = 0; + if(!name) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_credtype); + for(i=0;i <= KCDB_CREDTYPE_MAX_ID; i++) { + if(kcdb_credtype_tbl[i] && !wcscmp(name, kcdb_credtype_tbl[i]->ct.name)) + break; + } + LeaveCriticalSection(&cs_credtype); + + if(i <= KCDB_CREDTYPE_MAX_ID) { + *id = i; + return KHM_ERROR_SUCCESS; + } else + return KHM_ERROR_NOT_FOUND; +} + +khm_int32 kcdb_credtype_get_next_free_id(khm_int32 * id) +{ + int i; + + EnterCriticalSection(&cs_credtype); + for(i=0;i <= KCDB_CREDTYPE_MAX_ID; i++) { + if(!kcdb_credtype_tbl[i]) + break; + } + LeaveCriticalSection(&cs_credtype); + + if(i <= KCDB_CREDTYPE_MAX_ID) { + *id = i; + return KHM_ERROR_SUCCESS; + } else { + *id = -1; + return KHM_ERROR_NO_RESOURCES; + } +} + +khm_int32 kcdb_credtype_hold(kcdb_credtype_i * ict) { + + if(!ict) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_credtype); + ict->refcount++; + LeaveCriticalSection(&cs_credtype); + return KHM_ERROR_SUCCESS; +} + +khm_int32 kcdb_credtype_release(kcdb_credtype_i * ict) { + + if(!ict) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_credtype); + ict->refcount--; + kcdb_credtype_check_and_delete(ict->ct.id); + LeaveCriticalSection(&cs_credtype); + return KHM_ERROR_SUCCESS; +} + +void kcdb_credtype_msg_completion(kmq_message * m) +{ + kcdb_credtype_release((kcdb_credtype_i *) m->vparam); +} + +void kcdb_credtype_post_message(khm_int32 op, kcdb_credtype * type) +{ + kcdb_credtype_hold((kcdb_credtype_i *) type); + kmq_post_message(KMSG_KCDB, KMSG_KCDB_CREDTYPE, op, (void *) type); +} diff --git a/src/windows/identity/kcreddb/credtype.h b/src/windows/identity/kcreddb/credtype.h new file mode 100644 index 000000000..6e46db303 --- /dev/null +++ b/src/windows/identity/kcreddb/credtype.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KCDB_CREDTYPE_H +#define __KHIMAIRA_KCDB_CREDTYPE_H + +/* credtype */ +typedef struct kcdb_credtype_i_t { + kcdb_credtype ct; + khm_int32 refcount; + khm_int32 flags; + + struct kcdb_credtype_i_t * next; + struct kcdb_credtype_i_t * prev; +} kcdb_credtype_i; + +#define KCDB_CTI_FLAG_DELETED 8 + +extern CRITICAL_SECTION cs_credtype; +extern kcdb_credtype_i * kcdb_credtypes; +extern kcdb_credtype_i ** kcdb_credtype_tbl; + +void kcdb_credtype_init(void); +void kcdb_credtype_exit(void); +void kcdb_credtype_check_and_delete(khm_int32 id); +khm_int32 kcdb_credtype_hold(kcdb_credtype_i * ict); +khm_int32 kcdb_credtype_release(kcdb_credtype_i * ict); +void kcdb_credtype_msg_completion(kmq_message * m); +void kcdb_credtype_post_message(khm_int32 op, kcdb_credtype * type); +khm_int32 kcdb_credtype_get_next_free_id(khm_int32 * id); + +#endif \ No newline at end of file diff --git a/src/windows/identity/kcreddb/identity.c b/src/windows/identity/kcreddb/identity.c new file mode 100644 index 000000000..d6ae129d6 --- /dev/null +++ b/src/windows/identity/kcreddb/identity.c @@ -0,0 +1,1537 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include + +CRITICAL_SECTION cs_ident; +hashtable * kcdb_identities_namemap = NULL; +khm_int32 kcdb_n_identities = 0; +kcdb_identity * kcdb_identities = NULL; +kcdb_identity * kcdb_def_identity = NULL; +khm_handle kcdb_ident_sub = NULL; /* identity provider */ +khm_int32 kcdb_ident_cred_type = KCDB_CREDTYPE_INVALID; +/* primary credentials type */ +khm_ui_4 kcdb_ident_refresh_cycle = 0; +khm_boolean kcdb_checked_config = FALSE; + +KHMEXP khm_int32 KHMAPI +kcdb_identity_set_provider(khm_handle sub) +{ + EnterCriticalSection(&cs_ident); + if (sub != kcdb_ident_sub) { + if(kcdb_ident_sub != NULL) { + kmq_post_sub_msg(kcdb_ident_sub, + KMSG_IDENT, + KMSG_IDENT_EXIT, + 0, + 0); + kmq_delete_subscription(kcdb_ident_sub); + } + kcdb_ident_sub = sub; + + if (kcdb_ident_sub) + kmq_post_sub_msg(kcdb_ident_sub, + KMSG_IDENT, + KMSG_IDENT_INIT, + 0, + 0); + } + LeaveCriticalSection(&cs_ident); + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_provider(khm_handle * sub) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + + EnterCriticalSection(&cs_ident); + if(kcdb_ident_sub != NULL) + rv = KHM_ERROR_SUCCESS; + else + rv = KHM_ERROR_NOT_FOUND; + if(sub != NULL) + *sub = kcdb_ident_sub; + LeaveCriticalSection(&cs_ident); + + return rv; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_set_type(khm_int32 cred_type) +{ + EnterCriticalSection(&cs_ident); + kcdb_ident_cred_type = cred_type; + LeaveCriticalSection(&cs_ident); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_type(khm_int32 * ptype) +{ + if (!ptype) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_ident); + *ptype = kcdb_ident_cred_type; + LeaveCriticalSection(&cs_ident); + + if (*ptype >= 0) + return KHM_ERROR_SUCCESS; + else + return KHM_ERROR_NOT_FOUND; +} + +/* message completion routine */ +void +kcdbint_ident_msg_completion(kmq_message * m) { + kcdb_identity_release(m->vparam); +} + +void +kcdbint_ident_add_ref(const void * key, void * vid) { + /* References in the hashtable are not refcounted */ + + // kcdb_identity_hold(vid); +} + +void +kcdbint_ident_del_ref(const void * key, void * vid) { + /* References in the hashtable are not refcounted */ + + // kcdb_identity_release(vid); +} + +void +kcdbint_ident_init(void) { + InitializeCriticalSection(&cs_ident); + kcdb_identities_namemap = hash_new_hashtable( + KCDB_IDENT_HASHTABLE_SIZE, + hash_string, + hash_string_comp, + kcdbint_ident_add_ref, + kcdbint_ident_del_ref); +} + +void +kcdbint_ident_exit(void) { + EnterCriticalSection(&cs_ident); + hash_del_hashtable(kcdb_identities_namemap); + LeaveCriticalSection(&cs_ident); + DeleteCriticalSection(&cs_ident); +} + +KHMEXP khm_boolean KHMAPI +kcdb_identity_is_valid_name(const wchar_t * name) +{ + khm_int32 rv; + + /* special case. Note since the string we are comparing with is + of a known length we don't need to check the length of name. */ + if (!wcscmp(name, L"_Schema")) + return FALSE; + + rv = kcdb_identpro_validate_name(name); + + if(rv == KHM_ERROR_NO_PROVIDER || + rv == KHM_ERROR_NOT_IMPLEMENTED) + return TRUE; + else + return KHM_SUCCEEDED(rv); +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_create(const wchar_t *name, + khm_int32 flags, + khm_handle * result) { + kcdb_identity * id; + kcdb_identity * id_tmp; + size_t namesize; + + if(!result || !name) + return KHM_ERROR_INVALID_PARM; + + *result = NULL; + + /* is it there already? */ + EnterCriticalSection(&cs_ident); + id = hash_lookup(kcdb_identities_namemap, (void *) name); + if(id) + kcdb_identity_hold((khm_handle) id); + LeaveCriticalSection(&cs_ident); + + if(id) { + *result = (khm_handle) id; + return KHM_ERROR_SUCCESS; + } else if(!(flags & KCDB_IDENT_FLAG_CREATE)) { + return KHM_ERROR_NOT_FOUND; + } + + flags &= ~KCDB_IDENT_FLAG_CREATE; + + /* nope. create it */ + if((flags & ~KCDB_IDENT_FLAGMASK_RDWR) || + (flags & (KCDB_IDENT_FLAG_DEFAULT | + KCDB_IDENT_FLAG_SEARCHABLE))) { + /* can't specify this flag in create */ + return KHM_ERROR_INVALID_PARM; + } + + if(!kcdb_identity_is_valid_name(name)) { + return KHM_ERROR_INVALID_NAME; + } + + /* we expect the following will succeed since the above + test passed */ + StringCbLength(name, KCDB_IDENT_MAXCB_NAME, &namesize); + namesize += sizeof(wchar_t); + + id = malloc(sizeof(kcdb_identity)); + ZeroMemory(id, sizeof(kcdb_identity)); + id->magic = KCDB_IDENT_MAGIC; + id->name = malloc(namesize); + StringCbCopy(id->name, namesize, name); + + id->flags = (flags & KCDB_IDENT_FLAGMASK_LOCAL); + id->flags |= KCDB_IDENT_FLAG_ACTIVE; + LINIT(id); + + EnterCriticalSection(&cs_ident); + id_tmp = hash_lookup(kcdb_identities_namemap, (void *) id->name); + if(id_tmp) { + /* lost a race */ + kcdb_identity_hold((khm_handle) id_tmp); + *result = (khm_handle) id_tmp; + + free(id->name); + free(id); + + id = NULL; + } else { + khm_handle h_cfg; + + kcdb_identity_hold((khm_handle) id); + hash_add(kcdb_identities_namemap, + (void *) id->name, + (void *) id); + LPUSH(&kcdb_identities, id); + + if(KHM_SUCCEEDED(kcdb_identity_get_config((khm_handle) id, + 0, + &h_cfg))) { + /* don't need to set the KCDB_IDENT_FLAG_CONFIG flags + since kcdb_identity_get_conifg() sets it for us. */ + khm_int32 sticky; + + if (KHM_SUCCEEDED(khc_read_int32(h_cfg, L"Sticky", &sticky)) && + sticky) { + id->flags |= KCDB_IDENT_FLAG_STICKY; + } + + khc_close_space(h_cfg); + } + } + LeaveCriticalSection(&cs_ident); + + if(id != NULL) { + *result = (khm_handle) id; + + kcdb_identpro_notify_create((khm_handle) id); + + kcdbint_ident_post_message(KCDB_OP_INSERT, id); + } + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_delete(khm_handle vid) { + kcdb_identity * id; + khm_int32 code = KHM_ERROR_SUCCESS; + + EnterCriticalSection(&cs_ident); + if(!kcdb_is_identity(vid)) { + code = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + id = (kcdb_identity *) vid; + + if (kcdb_is_active_identity(vid)) { + + id->flags &= ~KCDB_IDENT_FLAG_ACTIVE; + + hash_del(kcdb_identities_namemap, (void *) id->name); + + LeaveCriticalSection(&cs_ident); + + kcdbint_ident_post_message(KCDB_OP_DELETE, id); + + /* Once everybody finishes dealing with the identity deletion, + we will get called again. */ + return KHM_ERROR_SUCCESS; + } else if (id->refcount == 0) { + /* If the identity is not active, it is not in the hashtable + either */ + LDELETE(&kcdb_identities, id); + + if (id->name) + free(id->name); + free(id); + } + /* else, we have an identity that is not active, but has + outstanding references. We have to wait until those references + are freed. Once they are released, kcdb_identity_delete() will + be called again. */ + +#if 0 + EnterCriticalSection(&cs_ident); + if(id->refcount == 0) { + /*TODO: free up the identity */ + } +#endif + _exit: + LeaveCriticalSection(&cs_ident); + + return code; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_set_flags(khm_handle vid, + khm_int32 flag) { + kcdb_identity * id; + khm_int32 oldflags; + khm_int32 newflags; + khm_int32 delta = 0; + khm_int32 rv; + + if(!kcdb_is_active_identity(vid)) + return KHM_ERROR_INVALID_PARM; + + id = (kcdb_identity *) vid; + + if((flag & ~(KCDB_IDENT_FLAGMASK_RDWR | KCDB_IDENT_FLAG_INVERT)) || + ((flag & KCDB_IDENT_FLAG_INVALID) && (flag & KCDB_IDENT_FLAG_VALID))) + return KHM_ERROR_INVALID_PARM; + + if(flag & KCDB_IDENT_FLAG_DEFAULT) { + /* kcdb_identity_set_default already does checking for + redundant transitions */ + rv = kcdb_identity_set_default((flag & KCDB_IDENT_FLAG_INVERT)?NULL: vid); + + if(KHM_FAILED(rv)) + return rv; + + flag &= ~KCDB_IDENT_FLAG_DEFAULT; + } + + if(flag & KCDB_IDENT_FLAG_SEARCHABLE) { + if(flag & KCDB_IDENT_FLAG_INVERT) { + EnterCriticalSection(&cs_ident); + if(id->flags & KCDB_IDENT_FLAG_SEARCHABLE) { + LeaveCriticalSection(&cs_ident); + rv = kcdb_identpro_set_searchable(vid, FALSE); + EnterCriticalSection(&cs_ident); + if(rv == KHM_ERROR_NO_PROVIDER || + KHM_SUCCEEDED(rv)) { + id->flags &= ~KCDB_IDENT_FLAG_SEARCHABLE; + delta |= KCDB_IDENT_FLAG_SEARCHABLE; + } + } + LeaveCriticalSection(&cs_ident); + } else { + EnterCriticalSection(&cs_ident); + if(!(id->flags & KCDB_IDENT_FLAG_SEARCHABLE)) { + LeaveCriticalSection(&cs_ident); + rv = kcdb_identpro_set_searchable(vid, TRUE); + EnterCriticalSection(&cs_ident); + if(rv == KHM_ERROR_NO_PROVIDER || + KHM_SUCCEEDED(rv)) { + id->flags |= KCDB_IDENT_FLAG_SEARCHABLE; + delta |= KCDB_IDENT_FLAG_SEARCHABLE; + } + } + LeaveCriticalSection(&cs_ident); + } + + flag &= ~KCDB_IDENT_FLAG_SEARCHABLE; + } + + /* deal with every other flag */ + + EnterCriticalSection(&cs_ident); + oldflags = id->flags; + if(flag & KCDB_IDENT_FLAG_INVERT) { + flag &= ~KCDB_IDENT_FLAG_INVERT; + id->flags &= ~flag; + } else { + id->flags |= flag; + + if(flag & KCDB_IDENT_FLAG_VALID) + id->flags &= ~KCDB_IDENT_FLAG_INVALID; + if(flag & KCDB_IDENT_FLAG_INVALID) + id->flags &= ~KCDB_IDENT_FLAG_VALID; + } + newflags = id->flags; + LeaveCriticalSection(&cs_ident); + delta |= newflags ^ oldflags; + + if((delta & KCDB_IDENT_FLAG_HIDDEN)) { + kcdbint_ident_post_message( + (newflags & KCDB_IDENT_FLAG_HIDDEN)?KCDB_OP_HIDE:KCDB_OP_UNHIDE, + vid); + } + + if((delta & KCDB_IDENT_FLAG_SEARCHABLE)) { + kcdbint_ident_post_message( + (newflags & KCDB_IDENT_FLAG_SEARCHABLE)?KCDB_OP_SETSEARCH:KCDB_OP_UNSETSEARCH, + vid); + } + + if(delta != 0) + kcdbint_ident_post_message(KCDB_OP_MODIFY, vid); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_flags(khm_handle vid, + khm_int32 * flags) { + kcdb_identity * id; + + *flags = 0; + + if(!kcdb_is_active_identity(vid)) + return KHM_ERROR_INVALID_PARM; + + id = (kcdb_identity *) vid; + + *flags = id->flags; + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_name(khm_handle vid, + wchar_t * buffer, + khm_size * pcbsize) { + size_t namesize; + kcdb_identity * id; + + if(!kcdb_is_active_identity(vid) || !pcbsize) + return KHM_ERROR_INVALID_PARM; + + id = (kcdb_identity *) vid; + + if(FAILED(StringCbLength(id->name, KCDB_IDENT_MAXCB_NAME, &namesize))) + return KHM_ERROR_UNKNOWN; + + namesize += sizeof(wchar_t); + + if(!buffer || namesize > *pcbsize) { + *pcbsize = namesize; + return KHM_ERROR_TOO_LONG; + } + + StringCbCopy(buffer, *pcbsize, id->name); + *pcbsize = namesize; + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_default(khm_handle * pvid) { + khm_handle def; + + if (pvid == NULL) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_ident); + def = kcdb_def_identity; + if (def != NULL) + kcdb_identity_hold(def); + LeaveCriticalSection(&cs_ident); + + *pvid = def; + + if (def != NULL) + return KHM_ERROR_SUCCESS; + else + return KHM_ERROR_NOT_FOUND; +} + +static khm_int32 +kcdbint_ident_set_default(khm_handle vid, + khm_boolean invoke_identpro) { + kcdb_identity * new_def; + kcdb_identity * old_def; + khm_int32 rv; + + if(vid != NULL && !kcdb_is_active_identity(vid)) + return KHM_ERROR_INVALID_PARM; + + new_def = (kcdb_identity *)vid; + + if(new_def != NULL && (new_def->flags & KCDB_IDENT_FLAG_DEFAULT)) + return KHM_ERROR_SUCCESS; + + if(new_def == NULL && kcdb_def_identity == NULL) + return KHM_ERROR_SUCCESS; + + /* first check with the identity provider if this operation + is permitted. */ + if (invoke_identpro) { + rv = kcdb_identpro_set_default(vid); + if(rv != KHM_ERROR_NO_PROVIDER && KHM_FAILED(rv)) + return rv; + } + + EnterCriticalSection(&cs_ident); + + old_def = kcdb_def_identity; + kcdb_def_identity = new_def; + + if(old_def != new_def) { + if(old_def) { + old_def->flags &= ~KCDB_IDENT_FLAG_DEFAULT; + kcdb_identity_release((khm_handle) old_def); + } + + if(new_def) { + new_def->flags |= KCDB_IDENT_FLAG_DEFAULT; + kcdb_identity_hold((khm_handle) new_def); + } + + LeaveCriticalSection(&cs_ident); + + if (invoke_identpro) + kcdbint_ident_post_message(KCDB_OP_NEW_DEFAULT, new_def); + } else { + LeaveCriticalSection(&cs_ident); + } + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_set_default(khm_handle vid) { + return kcdbint_ident_set_default(vid, TRUE); +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_set_default_int(khm_handle vid) { + return kcdbint_ident_set_default(vid, FALSE); +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_config(khm_handle vid, + khm_int32 flags, + khm_handle * result) { + khm_handle hkcdb; + khm_handle hidents = NULL; + khm_handle hident = NULL; + khm_int32 rv; + kcdb_identity * id; + + if(kcdb_is_active_identity(vid)) { + id = (kcdb_identity *) vid; + } else { + return KHM_ERROR_INVALID_PARM; + } + + hkcdb = kcdb_get_config(); + if(hkcdb) { + rv = khc_open_space(hkcdb, L"Identity", 0, &hidents); + if(KHM_FAILED(rv)) + goto _exit; + + rv = khc_open_space(hidents, + id->name, + flags | KCONF_FLAG_NOPARSENAME, + &hident); + + if(KHM_FAILED(rv)) + goto _exit; + + EnterCriticalSection(&cs_ident); + id->flags |= KCDB_IDENT_FLAG_CONFIG; + LeaveCriticalSection(&cs_ident); + + *result = hident; + } else + rv = KHM_ERROR_UNKNOWN; + +_exit: + if(hidents) + khc_close_space(hidents); + if(hkcdb) + khc_close_space(hkcdb); + return rv; +} + +/*! \note cs_ident must be available. */ +void +kcdbint_ident_post_message(khm_int32 op, kcdb_identity * id) { + kcdb_identity_hold(id); + kmq_post_message(KMSG_KCDB, KMSG_KCDB_IDENT, op, (void *) id); +} + +/*! \note cs_ident must be available. */ +KHMEXP khm_int32 KHMAPI +kcdb_identity_hold(khm_handle vid) { + kcdb_identity * id; + if(kcdb_is_active_identity(vid)) { + id = vid; + InterlockedIncrement(&(id->refcount)); + } else + return KHM_ERROR_INVALID_PARM; + return ERROR_SUCCESS; +} + +/*! \note cs_ident must be available. */ +KHMEXP khm_int32 KHMAPI +kcdb_identity_release(khm_handle vid) { + kcdb_identity * id; + khm_int32 refcount; + if(kcdb_is_identity(vid)) { + id = vid; + refcount = InterlockedDecrement(&(id->refcount)); + if(refcount == 0) { + EnterCriticalSection(&cs_ident); + /* We only delete identities which do not have a + configuration. */ + if (id->refcount == 0 && + !(id->flags & KCDB_IDENT_FLAG_CONFIG)) + kcdb_identity_delete(vid); + LeaveCriticalSection(&cs_ident); + } + } else + return KHM_ERROR_INVALID_PARM; + return ERROR_SUCCESS; +} + +struct kcdb_idref_result { + kcdb_identity * ident; + khm_int32 flags; + khm_size count; +}; + +static khm_int32 KHMAPI +kcdbint_idref_proc(khm_handle cred, void * r) { + khm_handle vid; + struct kcdb_idref_result *result; + khm_int32 flags; + + result = (struct kcdb_idref_result *) r; + + if (KHM_SUCCEEDED(kcdb_cred_get_identity(cred, &vid))) { + if (result->ident == (kcdb_identity *) vid) { + result->count++; + kcdb_cred_get_flags(cred, &flags); + + if (flags & KCDB_CRED_FLAG_RENEWABLE) { + result->flags |= KCDB_IDENT_FLAG_CRED_RENEW; + if (flags & KCDB_CRED_FLAG_INITIAL) { + result->flags |= KCDB_IDENT_FLAG_RENEWABLE; + } + } + + if (flags & KCDB_CRED_FLAG_EXPIRED) { + result->flags |= KCDB_IDENT_FLAG_CRED_EXP; + if (flags & KCDB_CRED_FLAG_INITIAL) { + result->flags |= KCDB_IDENT_FLAG_EXPIRED; + } + } + + if (flags & KCDB_CRED_FLAG_INITIAL) { + result->flags |= KCDB_IDENT_FLAG_VALID; + } + } + + kcdb_identity_release(vid); + } + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_refresh(khm_handle vid) { + kcdb_identity * ident; + khm_int32 code = KHM_ERROR_SUCCESS; + struct kcdb_idref_result result; + khm_int32 flags; + + EnterCriticalSection(&cs_ident); + + if (!kcdb_is_active_identity(vid)) { + code = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + ident = (kcdb_identity *) vid; + + result.ident = ident; + result.flags = 0; + result.count = 0; + + kcdb_credset_apply(NULL, kcdbint_idref_proc, &result); + + if (result.count == 0) + result.flags |= KCDB_IDENT_FLAG_EMPTY; + + kcdb_identity_set_flags(vid, result.flags); + kcdb_identity_get_flags(vid, &flags); + flags &= KCDB_IDENT_FLAGMASK_RDWR; + flags &= ~(KCDB_IDENT_FLAG_DEFAULT | + KCDB_IDENT_FLAG_SEARCHABLE); + flags ^= result.flags; + if (flags != 0) + kcdb_identity_set_flags(vid, flags | KCDB_IDENT_FLAG_INVERT); + + ident->refresh_cycle = kcdb_ident_refresh_cycle; + + _exit: + LeaveCriticalSection(&cs_ident); + + if (code == 0) + code = kcdb_identpro_update(vid); + + return code; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_refresh_all(void) { + kcdb_identity * ident; + khm_int32 code = KHM_ERROR_SUCCESS; + int hit_count; + + EnterCriticalSection(&cs_ident); + + kcdb_ident_refresh_cycle++; + + /* The do-while loop is here to account for race conditions. We + release cs_ident in the for loop, so we don't actually have a + guarantee that we traversed the whole identity list at the end. + We repeat until all the identities are uptodate. */ + + do { + hit_count = 0; + + for (ident = kcdb_identities; + ident != NULL; + ident = LNEXT(ident)) { + + if (!kcdb_is_active_identity(ident) || + ident->refresh_cycle == kcdb_ident_refresh_cycle) + continue; + + kcdb_identity_hold((khm_handle) ident); + + LeaveCriticalSection(&cs_ident); + kcdb_identity_refresh((khm_handle) ident); + EnterCriticalSection(&cs_ident); + + kcdb_identity_release((khm_handle) ident); + + hit_count++; + } + } while (hit_count > 0); + + LeaveCriticalSection(&cs_ident); + + return code; +} + +/*****************************************/ +/* Custom property functions */ + +KHMEXP khm_int32 KHMAPI +kcdb_identity_set_attr(khm_handle vid, + khm_int32 attr_id, + void * buffer, + khm_size cbbuf) +{ + kcdb_identity * id; + kcdb_attrib * attrib; + kcdb_type * type; + khm_size slot; + khm_size cbdest; + khm_int32 code = KHM_ERROR_SUCCESS; + + EnterCriticalSection(&cs_ident); + if(!kcdb_is_active_identity(vid)) { + LeaveCriticalSection(&cs_ident); + return KHM_ERROR_INVALID_PARM; + } + + id = (kcdb_identity *) vid; + + if(!(id->flags & KCDB_IDENT_FLAG_ATTRIBS)) { + kcdb_buf_new(&id->buf, KCDB_BUF_DEFAULT); + id->flags |= KCDB_IDENT_FLAG_ATTRIBS; + } + + if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) { + LeaveCriticalSection(&cs_ident); + return KHM_ERROR_INVALID_PARM; + } + +#if 0 + /* actually, even if an attribute is computed, we still allow + those values to be set. This is because computing values + is only for credentials. If a computed value is used as a + property in any other object, it is treated as a regular value + */ + if(attrib->flags & KCDB_ATTR_FLAG_COMPUTED) + { + LeaveCriticalSection(&cs_ident); + kcdb_attrib_release_info(attrib); + return KHM_ERROR_INVALID_OPERATION; + } +#endif + + if (buffer == NULL) { + /* we are removing a value */ + slot = kcdb_buf_slot_by_id(&id->buf, attr_id); + if (slot != KCDB_BUF_INVALID_SLOT && + kcdb_buf_exist(&id->buf, slot)) + kcdb_buf_alloc(&id->buf, slot, attr_id, 0); + code = KHM_ERROR_SUCCESS; + goto _exit; + } + + if(KHM_FAILED(kcdb_type_get_info(attrib->type, &type))) { + LeaveCriticalSection(&cs_ident); + kcdb_attrib_release_info(attrib); + return KHM_ERROR_INVALID_PARM; + } + + if(!(type->isValid(buffer,cbbuf))) { + code = KHM_ERROR_TYPE_MISMATCH; + goto _exit; + } + + if((type->dup(buffer, cbbuf, NULL, &cbdest)) != KHM_ERROR_TOO_LONG) { + code = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + kcdb_buf_alloc(&id->buf, KCDB_BUF_APPEND, attr_id, cbdest); + slot = kcdb_buf_slot_by_id(&id->buf, attr_id); + if(slot == KCDB_BUF_INVALID_SLOT || !kcdb_buf_exist(&id->buf, slot)) { + code = KHM_ERROR_NO_RESOURCES; + goto _exit; + } + + if(KHM_FAILED(code = + type->dup(buffer, cbbuf, kcdb_buf_get(&id->buf, slot), &cbdest))) + { + kcdb_buf_alloc(&id->buf, slot, attr_id, 0); + goto _exit; + } + + kcdb_buf_set_value_flag(&id->buf, slot); + +_exit: + LeaveCriticalSection(&cs_ident); + + if(attrib) + kcdb_attrib_release_info(attrib); + if(type) + kcdb_type_release_info(type); + + return code; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_set_attrib( + khm_handle vid, + wchar_t * attr_name, + void * buffer, + khm_size cbbuf) +{ + khm_int32 attr_id = -1; + + if(KHM_FAILED(kcdb_attrib_get_id(attr_name, &attr_id))) + return KHM_ERROR_INVALID_PARM; + + return kcdb_identity_set_attr( + vid, + attr_id, + buffer, + cbbuf); +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_attr( + khm_handle vid, + khm_int32 attr_id, + khm_int32 * attr_type, + void * buffer, + khm_size * pcbbuf) +{ + khm_int32 code = KHM_ERROR_SUCCESS; + kcdb_identity * id = NULL; + kcdb_attrib * attrib = NULL; + kcdb_type * type = NULL; + khm_size slot; + + if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) { + return KHM_ERROR_INVALID_PARM; + } + + if(KHM_FAILED(kcdb_type_get_info(attrib->type, &type))) { + kcdb_attrib_release_info(attrib); + return KHM_ERROR_UNKNOWN; + } + + if(attr_type) + *attr_type = attrib->type; + + EnterCriticalSection(&cs_ident); + + if(!kcdb_is_active_identity(vid)) { + code = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + id = (kcdb_identity *) vid; + + if(!(id->flags & KCDB_IDENT_FLAG_ATTRIBS) || + (slot = kcdb_buf_slot_by_id(&id->buf, attr_id)) == KCDB_BUF_INVALID_SLOT || + !kcdb_buf_val_exist(&id->buf, slot)) + { + code = KHM_ERROR_NOT_FOUND; + goto _exit; + } + + if(!buffer && !pcbbuf) { + /* in this case the caller is only trying to determine if the field + contains data. If we get here, then the value exists. */ + code = KHM_ERROR_SUCCESS; + goto _exit; + } + +#if 0 + if(attrib->flags & KCDB_ATTR_FLAG_COMPUTED) { + /* we should never hit this case */ +#ifdef DEBUG + assert(FALSE); +#else + code = KHM_ERROR_INVALID_OPERATION; +#endif + } else { +#endif + code = type->dup( + kcdb_buf_get(&id->buf, slot), + kcdb_buf_size(&id->buf, slot), + buffer, + pcbbuf); +#if 0 + } +#endif + +_exit: + LeaveCriticalSection(&cs_ident); + if(type) + kcdb_type_release_info(type); + if(attrib) + kcdb_attrib_release_info(attrib); + + return code; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_attrib( + khm_handle vid, + wchar_t * attr_name, + khm_int32 * attr_type, + void * buffer, + khm_size * pcbbuf) +{ + khm_int32 attr_id = -1; + + if(KHM_FAILED(kcdb_attrib_get_id(attr_name, &attr_id))) + return KHM_ERROR_NOT_FOUND; + + return kcdb_identity_get_attr( + vid, + attr_id, + attr_type, + buffer, + pcbbuf); +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_attr_string( + khm_handle vid, + khm_int32 attr_id, + wchar_t * buffer, + khm_size * pcbbuf, + khm_int32 flags) +{ + khm_int32 code = KHM_ERROR_SUCCESS; + kcdb_identity * id = NULL; + kcdb_attrib * attrib = NULL; + kcdb_type * type = NULL; + khm_size slot; + + if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) { + return KHM_ERROR_INVALID_PARM; + } + + if(KHM_FAILED(kcdb_type_get_info(attrib->type, &type))) { + kcdb_attrib_release_info(attrib); + return KHM_ERROR_UNKNOWN; + } + + EnterCriticalSection(&cs_ident); + + if(!kcdb_is_active_identity(vid)) { + code = KHM_ERROR_INVALID_PARM; + goto _exit; + } + + id = (kcdb_identity *) vid; + + if(!(id->flags & KCDB_IDENT_FLAG_ATTRIBS) || + (slot = kcdb_buf_slot_by_id(&id->buf, attr_id)) == KCDB_BUF_INVALID_SLOT || + !kcdb_buf_val_exist(&id->buf, slot)) + { + code = KHM_ERROR_NOT_FOUND; + goto _exit; + } + + if(!buffer && !pcbbuf) { + /* in this case the caller is only trying to determine if the field + contains data. If we get here, then the value exists */ + code = KHM_ERROR_SUCCESS; + goto _exit; + } + +#if 0 + if(attrib->flags & KCDB_ATTR_FLAG_COMPUTED) { +#ifdef DEBUG + assert(FALSE); +#else + code = KHM_ERROR_INVALID_OPERATION; +#endif + } else { +#endif + if(kcdb_buf_exist(&id->buf, slot)) { + code = type->toString( + kcdb_buf_get(&id->buf, slot), + kcdb_buf_size(&id->buf, slot), + buffer, + pcbbuf, + flags); + } else + code = KHM_ERROR_NOT_FOUND; +#if 0 + } +#endif + +_exit: + LeaveCriticalSection(&cs_ident); + if(type) + kcdb_type_release_info(type); + if(attrib) + kcdb_attrib_release_info(attrib); + + return code; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_attrib_string( + khm_handle vid, + wchar_t * attr_name, + wchar_t * buffer, + khm_size * pcbbuf, + khm_int32 flags) +{ + khm_int32 attr_id = -1; + + if(KHM_FAILED(kcdb_attrib_get_id(attr_name, &attr_id))) + return KHM_ERROR_NOT_FOUND; + + return kcdb_identity_get_attr_string( + vid, + attr_id, + buffer, + pcbbuf, + flags); +} + +/*****************************************/ +/* Identity provider interface functions */ + +KHMEXP khm_int32 KHMAPI +kcdb_identpro_validate_name(const wchar_t * name) +{ + kcdb_ident_name_xfer namex; + khm_handle sub; + khm_size cch; + khm_int32 rv = KHM_ERROR_SUCCESS; + + /* we need to verify the length and the contents of the string + before calling the identity provider */ + if(FAILED(StringCchLength(name, KCDB_IDENT_MAXCCH_NAME, &cch))) + return KHM_ERROR_TOO_LONG; + if(wcsspn(name, KCDB_IDENT_VALID_CHARS) != cch) + return KHM_ERROR_INVALID_NAME; + + EnterCriticalSection(&cs_ident); + if(kcdb_ident_sub != NULL) { + sub = kcdb_ident_sub; + } else { + sub = NULL; + rv = KHM_ERROR_NO_PROVIDER; + } + LeaveCriticalSection(&cs_ident); + + if(sub != NULL) { + ZeroMemory(&namex, sizeof(namex)); + + namex.name_src = name; + namex.result = KHM_ERROR_NOT_IMPLEMENTED; + + kmq_send_sub_msg(sub, + KMSG_IDENT, + KMSG_IDENT_VALIDATE_NAME, + 0, + (void *) &namex); + + rv = namex.result; + } + + return rv; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identpro_validate_identity(khm_handle identity) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + khm_handle sub; + + if(!kcdb_is_active_identity(identity)) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_ident); + if(kcdb_ident_sub != NULL) { + sub = kcdb_ident_sub; + } else { + sub = NULL; + rv = KHM_ERROR_NO_PROVIDER; + } + LeaveCriticalSection(&cs_ident); + + if(sub != NULL) { + rv = kmq_send_sub_msg(sub, + KMSG_IDENT, + KMSG_IDENT_VALIDATE_IDENTITY, + 0, + (void *) identity); + } + + return rv; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identpro_canon_name( + const wchar_t * name_in, + wchar_t * name_out, + khm_size * cb_name_out) +{ + khm_handle sub; + kcdb_ident_name_xfer namex; + wchar_t name_tmp[KCDB_IDENT_MAXCCH_NAME]; + khm_int32 rv = KHM_ERROR_SUCCESS; + khm_size cch; + + if(cb_name_out == 0 || + FAILED(StringCchLength(name_in, KCDB_IDENT_MAXCCH_NAME, &cch)) || + wcsspn(name_in, KCDB_IDENT_VALID_CHARS) != cch) + return KHM_ERROR_INVALID_NAME; + + EnterCriticalSection(&cs_ident); + if(kcdb_ident_sub != NULL) { + sub = kcdb_ident_sub; + } else { + sub = NULL; + rv = KHM_ERROR_NO_PROVIDER; + } + LeaveCriticalSection(&cs_ident); + + if(sub != NULL) { + ZeroMemory(&namex, sizeof(namex)); + ZeroMemory(name_tmp, sizeof(name_tmp)); + + namex.name_src = name_in; + namex.name_dest = name_tmp; + namex.cb_name_dest = sizeof(name_tmp); + namex.result = KHM_ERROR_NOT_IMPLEMENTED; + + rv = kmq_send_sub_msg( + sub, + KMSG_IDENT, + KMSG_IDENT_CANON_NAME, + 0, + (void *) &namex); + + if(KHM_SUCCEEDED(namex.result)) { + const wchar_t * name_result; + khm_size cb; + + if(name_in[0] != 0 && name_tmp[0] == 0) + name_result = name_tmp; + else + name_result = name_in; + + if(FAILED(StringCbLength(name_result, KCDB_IDENT_MAXCB_NAME, &cb))) + rv = KHM_ERROR_UNKNOWN; + else { + cb += sizeof(wchar_t); + if(name_out == 0 || *cb_name_out < cb) { + rv = KHM_ERROR_TOO_LONG; + *cb_name_out = cb; + } else { + StringCbCopy(name_out, *cb_name_out, name_result); + *cb_name_out = cb; + rv = KHM_ERROR_SUCCESS; + } + } + } + } + + return rv; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identpro_compare_name( + const wchar_t * name1, + const wchar_t * name2) +{ + khm_handle sub; + kcdb_ident_name_xfer namex; + khm_int32 rv = 0; + + EnterCriticalSection(&cs_ident); + if(kcdb_ident_sub != NULL) { + sub = kcdb_ident_sub; + } else { + sub = NULL; + /* Generally in kcdb_identpro_* functions we don't emulate + any behavior if the provider is not available, but lacking + a way to make this known, we emulate here */ + rv = wcscmp(name1, name2); + } + LeaveCriticalSection(&cs_ident); + + if(sub != NULL) { + ZeroMemory(&namex, sizeof(namex)); + namex.name_src = name1; + namex.name_alt = name2; + + kmq_send_sub_msg( + sub, + KMSG_IDENT, + KMSG_IDENT_COMPARE_NAME, + 0, + (void *) &namex); + + rv = namex.result; + } + + return rv; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identpro_set_default( + khm_handle identity) +{ + khm_handle sub; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if((identity != NULL) && + !kcdb_is_active_identity(identity)) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_ident); + if(kcdb_ident_sub != NULL) { + sub = kcdb_ident_sub; + } else { + sub = NULL; + rv = KHM_ERROR_NO_PROVIDER; + } + LeaveCriticalSection(&cs_ident); + + if(sub != NULL) { + rv = kmq_send_sub_msg( + sub, + KMSG_IDENT, + KMSG_IDENT_SET_DEFAULT, + (identity != NULL), + (void *) identity); + } + + return rv; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identpro_set_searchable( + khm_handle identity, + khm_boolean searchable) +{ + khm_handle sub; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(!kcdb_is_active_identity(identity)) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_ident); + if(kcdb_ident_sub != NULL) { + sub = kcdb_ident_sub; + } else { + sub = NULL; + rv = KHM_ERROR_NO_PROVIDER; + } + LeaveCriticalSection(&cs_ident); + + if(sub != NULL) { + rv = kmq_send_sub_msg( + sub, + KMSG_IDENT, + KMSG_IDENT_SET_SEARCHABLE, + searchable, + (void *) identity); + } + + return rv; +} + + +KHMEXP khm_int32 KHMAPI +kcdb_identpro_update(khm_handle identity) +{ + khm_handle sub; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(!kcdb_is_active_identity(identity)) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_ident); + if(kcdb_ident_sub != NULL) { + sub = kcdb_ident_sub; + } else { + sub = NULL; + rv = KHM_ERROR_NO_PROVIDER; + } + LeaveCriticalSection(&cs_ident); + + if(sub != NULL) { + rv = kmq_send_sub_msg( + sub, + KMSG_IDENT, + KMSG_IDENT_UPDATE, + 0, + (void *) identity); + } + + return rv; +} + +KHMEXP khm_int32 KHMAPI +kcdb_identpro_notify_create(khm_handle identity) +{ + khm_handle sub; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(!kcdb_is_active_identity(identity)) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_ident); + if(kcdb_ident_sub != NULL) { + sub = kcdb_ident_sub; + } else { + sub = NULL; + rv = KHM_ERROR_NO_PROVIDER; + } + LeaveCriticalSection(&cs_ident); + + if(sub != NULL) { + rv = kmq_send_sub_msg( + sub, + KMSG_IDENT, + KMSG_IDENT_NOTIFY_CREATE, + 0, + (void *) identity); + } + + return rv; +} + +KHMEXP khm_int32 KHMAPI kcdb_identpro_get_ui_cb(void * rock) +{ + khm_handle sub; + khm_int32 rv = KHM_ERROR_SUCCESS; + + EnterCriticalSection(&cs_ident); + if(kcdb_ident_sub != NULL) { + sub = kcdb_ident_sub; + } else { + sub = NULL; + rv = KHM_ERROR_NO_PROVIDER; + } + LeaveCriticalSection(&cs_ident); + + if(sub != NULL) { + rv = kmq_send_sub_msg( + sub, + KMSG_IDENT, + KMSG_IDENT_GET_UI_CALLBACK, + 0, + rock); + } + + return rv; +} + +KHMEXP khm_int32 KHMAPI kcdb_identity_enum( + khm_int32 and_flags, + khm_int32 eq_flags, + wchar_t * name_buf, + khm_size * pcb_buf, + khm_size * pn_idents) +{ + kcdb_identity * id; + khm_int32 rv = KHM_ERROR_SUCCESS; + khm_size cb_req = 0; + khm_size n_idents = 0; + size_t cb_curr; + size_t cch_curr; + size_t cch_left; + HRESULT hr; + + if ((name_buf == NULL && pcb_buf == NULL && pn_idents == NULL) || + (name_buf != NULL && pcb_buf == NULL)) + return KHM_ERROR_INVALID_PARM; + + eq_flags &= and_flags; + + EnterCriticalSection(&cs_ident); + + if (!kcdb_checked_config) { + khm_handle h_kcdb = NULL; + khm_handle h_idents = NULL; + khm_handle h_ident = NULL; + + h_kcdb = kcdb_get_config(); + if (!h_kcdb) + goto _config_check_cleanup; + if(KHM_FAILED(khc_open_space(h_kcdb, L"Identity", 0, &h_idents))) + goto _config_check_cleanup; + + while(KHM_SUCCEEDED(khc_enum_subspaces(h_idents, + h_ident, + &h_ident))) { + + wchar_t wname[KCDB_IDENT_MAXCCH_NAME]; + khm_size cb; + khm_handle t_id; + + cb = sizeof(wname); + if (KHM_FAILED(khc_get_config_space_name(h_ident, + wname, + &cb))) + continue; + + if (KHM_SUCCEEDED(kcdb_identity_create(wname, + KCDB_IDENT_FLAG_CREATE, + &t_id))) + kcdb_identity_release(t_id); + } + + _config_check_cleanup: + if (h_kcdb) + khc_close_space(h_kcdb); + if (h_idents) + khc_close_space(h_idents); + + kcdb_checked_config = TRUE; + } + + for ( id = kcdb_identities; + id != NULL; + id = LNEXT(id) ) { + if (((id->flags & KCDB_IDENT_FLAG_ACTIVE) == + KCDB_IDENT_FLAG_ACTIVE) && + ((id->flags & and_flags) == eq_flags)) { + n_idents ++; + hr = StringCbLength(id->name, KCDB_IDENT_MAXCB_NAME, &cb_curr); +#ifdef DEBUG + assert(SUCCEEDED(hr)); +#endif + cb_req += cb_curr + sizeof(wchar_t); + } + } + + cb_req += sizeof(wchar_t); + + if (pn_idents != NULL) + *pn_idents = n_idents; + + if (pcb_buf != NULL && (name_buf == NULL || *pcb_buf < cb_req)) { + *pcb_buf = cb_req; + + rv = KHM_ERROR_TOO_LONG; + } else if(name_buf != NULL) { + cch_left = (*pcb_buf) / sizeof(wchar_t); + + for (id = kcdb_identities; + id != NULL; + id = LNEXT(id)) { + if (((id->flags & KCDB_IDENT_FLAG_ACTIVE) == + KCDB_IDENT_FLAG_ACTIVE) && + ((id->flags & and_flags) == eq_flags)) { + StringCchLength(id->name, KCDB_IDENT_MAXCCH_NAME, + &cch_curr); + cch_curr++; + StringCchCopy(name_buf, cch_left, id->name); + cch_left -= cch_curr; + name_buf += cch_curr; + } + } + + *name_buf = L'\0'; + *pcb_buf = cb_req; + } + + LeaveCriticalSection(&cs_ident); + + return rv; +} diff --git a/src/windows/identity/kcreddb/identity.h b/src/windows/identity/kcreddb/identity.h new file mode 100644 index 000000000..6c7e26ee8 --- /dev/null +++ b/src/windows/identity/kcreddb/identity.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KCDB_IDENTITY_H +#define __KHIMAIRA_KCDB_IDENTITY_H + +/* Identity */ + +#define KCDB_IDENT_HASHTABLE_SIZE 31 + +typedef struct kcdb_identity_t { + khm_int32 magic; + wchar_t * name; + khm_int32 flags; + khm_int32 refcount; + kcdb_buf buf; + khm_ui_4 refresh_cycle; + struct kcdb_identity_t * next; + struct kcdb_identity_t * prev; +} kcdb_identity; + +#define KCDB_IDENT_MAGIC 0x31938d4f + +extern CRITICAL_SECTION cs_ident; +extern hashtable * kcdb_identities_namemap; +extern khm_int32 kcdb_n_identities; +extern kcdb_identity * kcdb_identities; /* all identities */ +extern kcdb_identity * kcdb_def_identity; /* default identity */ +extern khm_ui_4 kcdb_ident_refresh_cycle; + +void kcdbint_ident_init(void); +void kcdbint_ident_exit(void); +void kcdbint_ident_msg_completion(kmq_message * m); +void kcdbint_ident_post_message(khm_int32 op, kcdb_identity * id); + +#define kcdb_is_identity(id) ((id) && ((kcdb_identity *)(id))->magic == KCDB_IDENT_MAGIC) +#define kcdb_is_active_identity(id) (kcdb_is_identity(id) && (((kcdb_identity *)(id))->flags & KCDB_IDENT_FLAG_ACTIVE)) + +#endif diff --git a/src/windows/identity/kcreddb/init.c b/src/windows/identity/kcreddb/init.c new file mode 100644 index 000000000..2df3f3e3f --- /dev/null +++ b/src/windows/identity/kcreddb/init.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include + +/* set to TRUE when the configuration is loaded */ +static int kcdb_config_loaded = 0; + +/* global state cs */ +static CRITICAL_SECTION cs_kcdb_global; + +/* forward dcl */ +void KHMAPI kcdb_msg_completion(kmq_message * m); + +void kcdb_init(void) { + /* setup the critical sections */ + InitializeCriticalSection(&cs_kcdb_global); + + kmq_set_completion_handler(KMSG_KCDB, kcdb_msg_completion); + + kcdb_credtype_init(); + kcdbint_ident_init(); + kcdb_credset_init(); + kcdb_cred_init(); + kcdb_type_init(); + kcdb_attrib_init(); +} + +void kcdb_exit(void) { + + kcdb_attrib_exit(); + kcdb_type_exit(); + kcdb_cred_exit(); + kcdb_credset_exit(); + kcdbint_ident_exit(); + kcdb_credtype_exit(); + + kmq_set_completion_handler(KMSG_KCDB, NULL); + + DeleteCriticalSection(&cs_kcdb_global); +} + +khm_handle kcdb_get_config(void) { + khm_handle space = NULL; + + EnterCriticalSection(&cs_kcdb_global); + if(!kcdb_config_loaded) { + khc_load_schema(NULL, schema_kcdbconfig); + kcdb_config_loaded = 1; + } + khc_open_space(NULL, L"KCDB", 0, &space); + LeaveCriticalSection(&cs_kcdb_global); + + return space; +} + +void KHMAPI kcdb_msg_completion(kmq_message * m) { + if(!m) + return; + if(m->subtype == KMSG_KCDB_IDENT) + kcdbint_ident_msg_completion(m); + else if(m->subtype == KMSG_KCDB_ATTRIB) + kcdb_attrib_msg_completion(m); + else if(m->subtype == KMSG_KCDB_TYPE) + kcdb_type_msg_completion(m); + else if(m->subtype == KMSG_KCDB_CREDTYPE) + kcdb_credtype_msg_completion(m); +} diff --git a/src/windows/identity/kcreddb/kcdbconfig.csv b/src/windows/identity/kcreddb/kcdbconfig.csv new file mode 100644 index 000000000..bd1fc6f33 --- /dev/null +++ b/src/windows/identity/kcreddb/kcdbconfig.csv @@ -0,0 +1,15 @@ +Name,Type,Value,Description +KCDB,KC_SPACE,0,Khimaira Configuration DB + Identity,KC_SPACE,0,Configuration space for identities + _Schema,KC_SPACE,0,Schema for identities + Sticky,KC_INT32,0,Boolean. Is this a sticky identity? + Monitor,KC_INT32,1,Boolean. Enables monitoring the identity + WarnThreshold,KC_INT32,900,In seconds + AllowWarn,KC_INT32,1,Boolean. Allow warning. + CriticalThreshold,KC_INT32,60,In seconds + AllowCritical,KC_INT32,1,Boolean. Allow critical. + AutoRenewThreshold,KC_INT32,60,In seconds + AllowAutoRenew,KC_INT32,1,Boolean. + _Schema,KC_ENDSPACE,0, + Identity,KC_ENDSPACE,0, +KCDB,KC_ENDSPACE,0, diff --git a/src/windows/identity/kcreddb/kcreddb.h b/src/windows/identity/kcreddb/kcreddb.h new file mode 100644 index 000000000..e5be0f462 --- /dev/null +++ b/src/windows/identity/kcreddb/kcreddb.h @@ -0,0 +1,3212 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KCREDDB_H__ +#define __KHIMAIRA_KCREDDB_H__ + +#include +#include + + +/*! \defgroup kcdb NetIDMgr Credentials Database */ +/*@{*/ + +/*! \brief Maximum length in characters of short description + + The length includes the terminating \a NULL character. + */ +#define KCDB_MAXCCH_SHORT_DESC 256 + +/*! \brief Maximum length in bytes of short description + + The length includes the terminating \a NULL character. + */ +#define KCDB_MAXCB_SHORT_DESC (sizeof(wchar_t) * KCDB_MAXCCH_SHORT_DESC) + +/*! \brief Maximum length in characters of long description + + The length includes the terminating \a NULL character. + */ +#define KCDB_MAXCCH_LONG_DESC 8192 + +/*! \brief Maximum length in characters of long description + + The length includes the terminating \a NULL character. + */ +#define KCDB_MAXCB_LONG_DESC (sizeof(wchar_t) * KCDB_MAXCCH_LONG_DESC) + +/*! \brief Maximum length in characters of name + + The length includes the terminating \a NULL character. + */ +#define KCDB_MAXCCH_NAME 256 + +/*! \brief Maximum length in bytes of short description + + The length includes the terminating \a NULL character. + */ +#define KCDB_MAXCB_NAME (sizeof(wchar_t) * KCDB_MAXCCH_NAME) + +/*! \brief Automatically determine the number of bytes required + + Can be used in most places where a count of bytes is required. + For many objects, the number of bytes that are required can be + determined through context and may be ommited. In such cases you + can use the \a KCDB_CBSIZE_AUTO value to specify that the function + is to determine the size automatically. + + \note Not all functions that take a count of bytes support the \a + KCDB_CBSIZE_AUTO value. +*/ +#define KCDB_CBSIZE_AUTO (-1) + +/*! +\defgroup kcdb_ident Identities + +Functions, macros etc. for manipulating identities. +*/ + +/*@{*/ + +/*! \brief The maximum number of characters (including terminator) that can + be specified as an identity name */ +#define KCDB_IDENT_MAXCCH_NAME 256 + +/*! \brief The maximum number of bytes that can be specified as an identity + name */ +#define KCDB_IDENT_MAXCB_NAME (sizeof(wchar_t) * KCDB_IDENT_MAXCCH_NAME) + +/*! \brief Valid characters in an identity name */ +#define KCDB_IDENT_VALID_CHARS L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._@-/" + +/*! +\name Flags for identities */ +/*@{*/ + +/*! \brief Create the identity if it doesn't already exist. + \note Only to be used with kcdb_identity_create() */ +#define KCDB_IDENT_FLAG_CREATE 0x10000000L + +/*! \brief Inverts the accompanying flags. + + \note Only to be used with kcdb_identity_set_flags() + \see kcdb_identity_set_flags() */ +#define KCDB_IDENT_FLAG_INVERT 0x40000000L + +/*! \brief Has configuration information + + Indicates that the identity has persistent configuration + information associated with it. + */ +#define KCDB_IDENT_FLAG_CONFIG 0x00800000L + +/*! \brief Marks the identity as active. + + An active identity is one that is in active use within NetIDMgr. + + \note This flag is readonly and cannot be specified when creating + or modifying an identity. Once an identity is deleted, it will + no longer have this flag. */ +#define KCDB_IDENT_FLAG_ACTIVE 0x02000000L + +/*! \brief Sticky identity + + Sticky identities are identities that are always visible in the + credentials display even if no credentials are associated with it. + */ +#define KCDB_IDENT_FLAG_STICKY 0x04000000L + +/*! \brief The identity has custom attributes assigned + */ +#define KCDB_IDENT_FLAG_ATTRIBS 0x08000000L + +/*! \brief This is the default identity. + + At most one identity will have this flag set at any given time. + To set or reset the flag, use kcdb_identity_set_default() */ +#define KCDB_IDENT_FLAG_DEFAULT 0x00000001L + +/*! \brief This identity can be searched. + + The meaning of this flag is left to be interpreted by individual + plugins. */ +#define KCDB_IDENT_FLAG_SEARCHABLE 0x00000002L + +/*! \brief Hidden identity. + + The identity will not show up in the identity list window. Once + the hidden is switched off, the identity (and all associated + credentials) will re-appear in the window */ +#define KCDB_IDENT_FLAG_HIDDEN 0x00000004L + +/*! \brief Invalid identity + + For one reason or another, this identity is invalid. This flag + can be set by an identity provider to indicate that this identity + does not correspond to an actual identity because an external + entity (such as a KDC) has denied it's existence. + + The absence of this flag does not imply that the identity is + valid. The ::KCDB_IDENT_FLAG_VALID bit must be set for that to be + the case. If neither flag is set, then the status of the identity + is not known. +*/ +#define KCDB_IDENT_FLAG_INVALID 0x00000008L + +/*! \brief Valid identity + + The identity has been validated through an external entity, or + it's validity implied through the existence of credentials for the + identity. + + The absence of this flag does not imply that the identity is + invalid. The ::KCDB_IDENT_FLAG_INVALID bit must be set for that + to be the case. If neither flag is set, then the status of the + identity is not known. + */ +#define KCDB_IDENT_FLAG_VALID 0x00000010L + +/*! \brief Expired identity + + This identity has expired and can not be actively used to obtain + credentials. This determination is made based on the input of + some external entity. This flag may only be set by an identity + provider. +*/ +#define KCDB_IDENT_FLAG_EXPIRED 0x00000020L + +/*! \brief Empty identity + + The identity does not have actual credentials associated with it. + */ +#define KCDB_IDENT_FLAG_EMPTY 0x00000040L + +/*! \brief Renewable identity + + The initial credentials associated with this identity are + renewable. Thus making the whole identity renewable. + */ +#define KCDB_IDENT_FLAG_RENEWABLE 0x00000080L + +/*! \brief Required user interaction + + The identity is in a state which requires user interaction to + activate. Currently, the identity may not be in a state where it + can be used to obtain credentials. + + A typical example of this is when the primary password for an + identity has expired. + */ +#define KCDB_IDENT_FLAG_INTERACT 0x00000100L + +/*! \brief Has expired credentials + + The identity has expired credentials associated with it. + */ +#define KCDB_IDENT_FLAG_CRED_EXP 0x00000200L + +/*! \brief Has renewable credentials + + The identity has renewable credentials associated with it. If the + initial credentials of the identity are renewable, then identity + is renewable. Hence the ::KCDB_IDENT_FLAG_RENEWABLE should also + be set. + */ +#define KCDB_IDENT_FLAG_CRED_RENEW 0x00000400L + +/*! \brief Bit mask for local flags + + Local flags are those local to the KCDB. + */ +#define KCDB_IDENT_FLAGMASK_LOCAL 0x0000ffffL + +/*! \brief Read/write flags mask. + + A bitmask that correspond to all the read/write flags in the mask. +*/ +#define KCDB_IDENT_FLAGMASK_RDWR 0x000007ffL + +/*@}*/ + +/*! \name Identity Provider Data Structures +@{*/ + +/*! \brief Name transfer structure + + Used when the KCDB is communicating with the identity provider to + exchange string names of identities. See individual ::KMSG_IDENT + message subtypes for the usage of this structure. + */ +typedef struct tag_kcdb_ident_name_xfer { + const wchar_t * name_src; /*!< An identity name. Does not + exceed KCDB_IDENT_MAXCCH_NAME + characters including terminating + NULL. */ + const wchar_t * name_alt; /*!< An identity name. Does not + exceed KCDB_IDENT_MAXCCH_NAME + characters including terminating + NULL. */ + wchar_t * name_dest; /*!< Pointer to a buffer that is to + receive a response string. The + size of the buffer in bytes is + specified in \a cb_name_dest. */ + khm_size cb_name_dest; /*!< Size of buffer pointed to by \a + name_dest in bytes. */ + khm_int32 result; /*!< Receives a result value, which is + usually an error code defined in + kherror.h, though it is not + always. */ +} kcdb_ident_name_xfer; + +typedef struct tag_kcdb_ident_info { + khm_handle identity; + khm_int32 fields; + + FILETIME expiration; +} kcdb_ident_info; + +/*@}*/ + +/*! \name Identity provider interface functions + + These functions encapsulate safe calls to the current identity + provider. While these functions are exported, applications should + not call these functions directly. They are provided for use by + the NetIDMgr core application. +@{*/ + +/*! \brief Validate an identity name + + The name that is provided will be passed through sets of + validations. One set, which doesn't depend on the identity + provider checks whether the length of the identity name and + whether there are any invalid characters in the identity name. If + the name passes those tests, then the name is passed down to the + identity provider's name validation handler. + + \retval KHM_ERROR_SUCCESS The name is valid + \retval KHM_ERROR_TOO_LONG Too many characters in name + \retval KHM_ERROR_INVALID_NAME There were invalid characters in the name. + \retval KHM_ERROR_NO_PROVIDER There is no identity provider; + however the name passed the length and character tests. + \retval KHM_ERROR_NOT_IMPLEMENTED The identity provider doesn't + implement a name validation handler; however the name passed + the length and character tests. + + \see ::KMSG_IDENT_VALIDATE_NAME + */ +KHMEXP khm_int32 KHMAPI +kcdb_identpro_validate_name(const wchar_t * name); + +/*! \brief Validate an identity + + The identity itself needs to be validated. This may involve + communicating with an external entity. + + \see ::KMSG_IDENT_VALIDATE_IDENTITY + */ +KHMEXP khm_int32 KHMAPI +kcdb_identpro_validate_identity(khm_handle identity); + +/*! \brief Canonicalize the name + + + \see ::KMSG_IDENT_CANON_NAME +*/ +KHMEXP khm_int32 KHMAPI +kcdb_identpro_canon_name(const wchar_t * name_in, + wchar_t * name_out, + khm_size * cb_name_out); + +/*! \brief Compare two identity names + + \see ::KMSG_IDENT_COMPARE_NAME +*/ +KHMEXP khm_int32 KHMAPI +kcdb_identpro_compare_name(const wchar_t * name1, + const wchar_t * name2); + +/*! \brief Set the specified identity as the default + + \see ::KMSG_IDENT_SET_DEFAULT +*/ +KHMEXP khm_int32 KHMAPI +kcdb_identpro_set_default(khm_handle identity); + +/*! \brief Set the specified identity as searchable + + \see ::KMSG_IDENT_SET_SEARCHABLE +*/ +KHMEXP khm_int32 KHMAPI +kcdb_identpro_set_searchable(khm_handle identity, + khm_boolean searchable); + +/*! \brief Update the specified identity + + \see ::KMSG_IDENT_UPDATE +*/ +KHMEXP khm_int32 KHMAPI +kcdb_identpro_update(khm_handle identity); + +/*! \brief Obtain the UI callback + + \a rock is actually a pointer to a ::khui_ident_new_creds_cb which + is to receive the callback. + + \see ::KMSG_IDENT_GET_UI_CALLBACK + */ +KHMEXP khm_int32 KHMAPI +kcdb_identpro_get_ui_cb(void * rock); + +/*! \brief Notify an identity provider of the creation of a new identity + + \see ::KMSG_IDENT_NOTIFY_CREATE +*/ +KHMEXP khm_int32 KHMAPI +kcdb_identpro_notify_create(khm_handle identity); + +/*@}*/ + +/*! \brief Check if the given name is a valid identity name + + \return TRUE or FALSE to the question, is this valid? +*/ +KHMEXP khm_boolean KHMAPI +kcdb_identity_is_valid_name(const wchar_t * name); + +/*! \brief Create or open an identity. + + If the KCDB_IDENT_FLAG_CREATE flag is specified in the flags + parameter a new identity will be created if one does not already + exist with the given name. If an identity by that name already + exists, then the existing identity will be opened. The result + parameter will receive a held reference to the opened identity. + Use kcdb_identity_release() to release the handle. + + \param[in] name Name of identity to create + \param[in] flags If KCDB_IDENT_FLAG_CREATE is specified, then the + identity will be created if it doesn't already exist. + Additional flags can be set here which will be assigned to the + identity if it is created. Additional flags have no effect if + an existing identity is opened. + \param[out] result If the call is successful, this receives a held + reference to the identity. The caller should call + kcdb_identity_release() to release the identity once it is no + longer needed. + */ +KHMEXP khm_int32 KHMAPI +kcdb_identity_create(const wchar_t *name, + khm_int32 flags, + khm_handle * result); + +/*! \brief Mark an identity for deletion. + + The identity will be marked for deletion. The + KCDB_IDENT_FLAG_ACTIVE will no longer be present for this + identity. Once all references to the identity are released, it + will be removed from memory. All associated credentials will also + be removed. */ +KHMEXP khm_int32 KHMAPI +kcdb_identity_delete(khm_handle id); + +/*! \brief Set or unset the specified flags in the specified identity. + + Only flags that are in KCDB_IDENT_FLAGMASK_RDWR can be specifed + in the flags parameter. The only exception is the + KCDB_IDENT_FLAG_INVERT flag which controls whether the flags are + to be set or reset. + + If the ::KCDB_IDENT_FLAG_INVERT flag is not specified in \a flags, + then any flags set in \a flags will also be set in the identity. + If the ::KCDB_IDENT_FLAG_INVERT is specified, then any flag set in + \a flags will be reset in the identity. + + If ::KCDB_IDENT_FLAG_INVALID is set using this function, then the + ::KCDB_IDENT_FLAG_VALID will be automatically reset, and vice + versa. Resetting either bit does not undo this change, and will + leave the identity's validity unspecified. + + Note that setting or resetting certain flags have other semantic + side-effects: + + - ::KCDB_IDENT_FLAG_DEFAULT : Setting this is equivalent to + calling kcdb_identity_set_default() with \a id. Resetting this + is equivalent to calling kcdb_identity_set_default() with NULL. + + - ::KCDB_IDENT_FLAG_SEARCHABLE : Setting this will result in the + identity provider getting notified of the change. If the + identity provider indicates that searchable flag should not be + set or reset (according to KCDB_IDENT_FLAG_INVERT setting) on + the identity, then kcdb_identity_set_flags() will return an + error. + + \note kcdb_identity_set_flags() is not atomic. Even if the + function returns a failure code, some flags in the identity may + have been set. When calling kcdb_identity_set_flags() always + check the flags in the identity using kcdb_identity_get_flags() to + check which flags have been set and which have failed. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_identity_set_flags(khm_handle id, + khm_int32 flags); + +/*! \brief Return all the flags for the identity + + The returned flags may include internal flags. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_flags(khm_handle id, + khm_int32 * flags); + +/*! \brief Return the name of the identity + + \param[out] buffer Buffer to copy the identity name into. The + maximum size of an identity name is \a KCDB_IDENT_MAXCB_NAME. + If \a buffer is \a NULL, then the required size of the buffer + is returned in \a pcbsize. + + \param[in,out] pcbsize Size of buffer in bytes. */ +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_name(khm_handle id, + wchar_t * buffer, + khm_size * pcbsize); + +/*! \brief Set the specified identity as the default. + + Specifying NULL effectively makes none of the identities the + default. + + \see kcdb_identity_set_flags() +*/ +KHMEXP khm_int32 KHMAPI +kcdb_identity_set_default(khm_handle id); + +/*! \brief Mark the specified identity as the default. + + This API is reserved for use by identity providers as a means of + specifying which identity is default. The difference between + kcdb_identity_set_default() and kcdb_identity_set_default_int() is + in semantics. + + - kcdb_identity_set_default() is used to request the KCDB to + designate the specified identity as the default. When + processing the request, the KCDB invokes the identity provider + to do the necessary work to make the identity the default. + + - kcdb_identity_set_default_int() is used by the identity provider + to notify the KCDB that the specified identity is the default. + This does not result in the invocation of any other semantics to + make the identity the default other than releasing the previous + defualt identity and making the specified one the default. As + an additional side effect, the notification <::KMSG_KCDB, + ::KMSG_KCDB_IDENT, ::KCDB_OP_NEW_DEFAULT> will also not be sent. + */ +KHMEXP khm_int32 KHMAPI +kcdb_identity_set_default_int(khm_handle id); + +/*! \brief Get the default identity + + Obtain a held handle to the default identity if there is one. The + handle must be freed using kcdb_identity_release(). + + If there is no default identity, then the handle pointed to by \a + pvid is set to \a NULL and the function returns + KHM_ERROR_NOT_FOUND. */ +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_default(khm_handle * pvid); + +/*! \brief Get the configuration space for the identity. + + \param[in] id Identity for which the configuraiton space is requested + + \param[in] flags Flags used when calling khc_open_space(). If \a + flags specifies KHM_FLAG_CREATE, then the configuration space + is created. + + \param[out] result The resulting handle. If the call is + successful, this receives a handle to the configuration space. + Use khc_close_space() to close the handle. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_config(khm_handle id, + khm_int32 flags, + khm_handle * result); + +/*! \brief Hold a reference to an identity. + + A reference to an identity (a handle) is only valid while it is + held. \note Once the handle is released, it can not be + revalidated by calling kcdb_identity_hold(). Doing so would lead + to unpredictable consequences. */ +KHMEXP khm_int32 KHMAPI +kcdb_identity_hold(khm_handle id); + +/*! \brief Release a reference to an identity. + \see kcdb_identity_hold() */ +KHMEXP khm_int32 KHMAPI +kcdb_identity_release(khm_handle id); + +/*! \brief Set the identity provider subscription + + If there was a previous subscription, that subscription will be + automatically deleted. + + \param[in] sub New identity provider subscription +*/ +KHMEXP khm_int32 KHMAPI +kcdb_identity_set_provider(khm_handle sub); + +/*! \brief Set the primary credentials type + + The primary credentials type is designated by the identity + provider. As such, this function should only be called by an + identity provider. + */ +KHMEXP khm_int32 KHMAPI +kcdb_identity_set_type(khm_int32 cred_type); + +/*! \brief Retrieve the identity provider subscription + + \param[out] sub Receives the current identity provider + subscription. Set to NULL if only the existence of an + identity provider needs to be checked. + + \retval KHM_ERROR_SUCCESS An identity provider exists. If \a sub + was not NULL, the subscription has been copied there. + + \retval KHM_ERROR_NOT_FOUND There is currently no registered + identity provider. If \a sub was not NULL, the handle it + points to has been set to NULL. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_provider(khm_handle * sub); + +/*! \brief Retrieve the identity provider credentials type + + This is the credentials type that the identity provider has + designated as the primary credentials type. + */ +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_type(khm_int32 * ptype); + +/*! \brief Set an attribute in an identity by attribute id + + \param[in] buffer A pointer to a buffer containing the data to + assign to the attribute. Setting \a buffer to NULL has the + effect of removing any data that is already assigned to the + attribute. If \a buffer is non-NULL, then \a cbbuf should + specify the number of bytes in \a buffer. + + \param[in] cbbuf Number of bytes of data in \a buffer. The + individual data type handlers may copy in less than this many + bytes in to the credential. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_identity_set_attr(khm_handle identity, + khm_int32 attr_id, + void * buffer, + khm_size cbbuf); + +/*! \brief Set an attribute in an identity by name + + The attribute name has to be a KCDB registered attribute or + property. + + \param[in] cbbuf Number of bytes of data in \a buffer. The + individual data type handlers may copy in less than this many + bytes in to the credential. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_identity_set_attrib(khm_handle identity, + wchar_t * attr_name, + void * buffer, + khm_size cbbuf); + +/*! \brief Get an attribute from an identity by attribute id. + + \param[in] buffer The buffer that is to receive the attribute + value. Set this to NULL if only the required buffer size is + to be returned. + + \param[in,out] cbbuf The number of bytes available in \a buffer. + If \a buffer is not sufficient, returns KHM_ERROR_TOO_LONG and + sets this to the required buffer size. + + \param[out] attr_type Receives the data type of the attribute. + Set this to NULL if the type is not required. + + \note Set both \a buffer and \a cbbuf to NULL if only the + existence of the attribute is to be checked. If the attribute + exists in this identity then the function will return + KHM_ERROR_SUCCESS, otherwise it returns KHM_ERROR_NOT_FOUND. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_attr(khm_handle identity, + khm_int32 attr_id, + khm_int32 * attr_type, + void * buffer, + khm_size * pcbbuf); + +/*! \brief Get an attribute from an identity by name. + + \param[in] buffer The buffer that is to receive the attribute + value. Set this to NULL if only the required buffer size is + to be returned. + + \param[in,out] cbbuf The number of bytes available in \a buffer. + If \a buffer is not sufficient, returns KHM_ERROR_TOO_LONG and + sets this to the required buffer size. + + \note Set both \a buffer and \a cbbuf to NULL if only the + existence of the attribute is to be checked. If the attribute + exists in this identity then the function will return + KHM_ERROR_SUCCESS, otherwise it returns KHM_ERROR_NOT_FOUND. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_attrib(khm_handle identity, + wchar_t * attr_name, + khm_int32 * attr_type, + void * buffer, + khm_size * pcbbuf); + +/*! \brief Get the string representation of an identity attribute. + + A shortcut function which generates the string representation of + an identity attribute directly. + + \param[in] identity A handle to an identity + + \param[in] attr_id The attribute to retrieve + + \param[out] buffer A pointer to a string buffer which receives the + string form of the attribute. Set this to NULL if you only + want to determine the size of the required buffer. + + \param[in,out] pcbbuf A pointer to a #khm_int32 that, on entry, + holds the size of the buffer pointed to by \a buffer, and on + exit, receives the actual number of bytes that were copied. + + \param[in] flags Flags for the string conversion. Can be set to + one of KCDB_TS_LONG or KCDB_TS_SHORT. The default is + KCDB_TS_LONG. + + \retval KHM_ERROR_SUCCESS Success + \retval KHM_ERROR_NOT_FOUND The given attribute was either invalid + or was not defined for this identity + \retval KHM_ERROR_INVALID_PARM One or more parameters were invalid + \retval KHM_ERROR_TOO_LONG Either \a buffer was NULL or the + supplied buffer was insufficient +*/ +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_attr_string(khm_handle identity, + khm_int32 attr_id, + wchar_t * buffer, + khm_size * pcbbuf, + khm_int32 flags); + +/*! \brief Get the string representation of an identity attribute by name. + + A shortcut function which generates the string representation of + an identity attribute directly. + + \param[in] identity A handle to an identity + + \param[in] attrib The name of the attribute to retrieve + + \param[out] buffer A pointer to a string buffer which receives the + string form of the attribute. Set this to NULL if you only + want to determine the size of the required buffer. + + \param[in,out] pcbbuf A pointer to a #khm_int32 that, on entry, + holds the size of the buffer pointed to by \a buffer, and on + exit, receives the actual number of bytes that were copied. + + \param[in] flags Flags for the string conversion. Can be set to + one of KCDB_TS_LONG or KCDB_TS_SHORT. The default is + KCDB_TS_LONG. + + \see kcdb_identity_get_attr_string() +*/ +KHMEXP khm_int32 KHMAPI +kcdb_identity_get_attrib_string(khm_handle identity, + wchar_t * attr_name, + wchar_t * buffer, + khm_size * pcbbuf, + khm_int32 flags); + +/*! \brief Enumerate identities + + Enumerates all the active identities that match the criteria + specified using \a and_flags and \a eq_flags. The condition is + applied to all active identities as follows: + + \code + (identity->flags & and_flags) == (eq_flags & and_flags) + \endcode + + Essentially, if a flag is set in \a and_flags, then that flag in + the identity should equal the setting in \a eq_flags. + + \param[in] and_flags See above + + \param[in] eq_flags See above + + \param[out] name_buf Buffer to receive the list of identity names. + Can be NULL if only the required size of the buffer or the + number of matching identities is required. The list is + returned as a multi string. + + \param[in,out] pcb_buf Number of bytes in buffer pointed to by \a + name_buf on entry. On exit, will receive the number of bytes + copied. Can be NULL only if \a name_buf is also NULL. If \a + name_buf is NULL or if \a pcb_buf indicates that the buffer is + insufficient, this will receive the number of bytes required + and the return value of the function will be + KHM_ERROR_TOO_LONG + + \param[out] pn_idents Receives the number of identities that match + the given criteria. + + \retval KHM_ERROR_SUCCESS If \a name_buf was valid, the buffer now + contains a multi string of identities that matched. If \a + pn_idents was valid, it contains the number of identities + matched. + + \retval KHM_ERROR_TOO_LONG No buffer was supplied or the supplied + buffer was insufficient. If \a pn_idents was valid, it + contains the number of identities. + + \retval KHM_ERROR_INVALID_PARM None of the parameters \a name_buf, + \a pcb_buf and \a pn_idents were supplied, or \a pcb_buf was + NULL when \a name_buf was not. + + \note Calling this function to obtain the required size of the + buffer and then calling it with a that sized buffer is not + guaranteed to work since the list of identities may change + between the two calls. + */ +KHMEXP khm_int32 KHMAPI +kcdb_identity_enum(khm_int32 and_flags, + khm_int32 eq_flags, + wchar_t * name_buf, + khm_size * pcb_buf, + khm_size * pn_idents); + +/*! \brief Refresh identity attributes based on root credential set + + Several flags in an identity are dependent on the credentials that + are associated with it in the root credential set. In addition, + other flags in an identity depend on external factors that need to + be verfied once in a while. This API goes through the root + credential set as well as consulting the identity provider to + update an identity. + + \see kcdb_identity_refresh() + */ +KHMEXP khm_int32 KHMAPI +kcdb_identity_refresh(khm_handle vid); + +/*! \brief Refresh all identities + + Equivalent to calling kcdb_identity_refresh() for all active + identities. + + \see kcdb_identityt_refresh() + */ +KHMEXP khm_int32 KHMAPI +kcdb_identity_refresh_all(void); + +/* KSMG_KCDB_IDENT notifications are structured as follows: + type=KMSG_KCDB + subtype=KMSG_KCDB_IDENT + uparam=one of KCDB_OP_* + blob=handle to identity in question */ + +/*@}*/ + + +/*********************************************************************/ + + +/*! +\defgroup kcdb_creds Credential sets and individual credentials + +@{ +*/ + + +/*! \brief Credentials process function + + This function is called for each credential in a credential set + when supplied to kcdb_credset_apply(). It should return + KHM_ERROR_SUCCESS to continue the operation, or any other value to + terminate the processing. + + \see kcdb_credset_apply() +*/ +typedef khm_int32 +(KHMAPI *kcdb_cred_apply_func)(khm_handle cred, + void * rock); + +/*! \brief Credentials filter function. + + Should return non-zero if the credential passed as \a cred is to + be "accepted". The precise consequence of a non-zero return value + is determined by the individual function that this call back is + passed into. + + This function should not call any other function which may modify + \a cred. + + \see kcdb_credset_collect_filtered() + \see kcdb_credset_extract_filtered() +*/ +typedef khm_int32 +(KHMAPI *kcdb_cred_filter_func)(khm_handle cred, + khm_int32 flags, + void * rock); + +/*! \brief Credentials compare function. + + Asserts a weak ordering on the credentials that are passed in as + \a cred1 and \a cred2. It should return: + + - a negative value if \a cred1 < \a cred2 + - zero if \a cred1 == \a cred2 + - a postive value if \a cred1 > \a cred2 + \see kcdb_credset_sort() +*/ +typedef khm_int32 +(KHMAPI *kcdb_cred_comp_func)(khm_handle cred1, + khm_handle cred2, + void * rock); + +/*! \defgroup kcdb_credset Credential sets */ +/*@{*/ + +/*! \brief Create a credential set. + + Credential sets are temporary containers for credentials. These + can be used by plug-ins to store credentials while they are being + enumerated from an external source. Once all the credentials have + been collected into the credential set, the plug-in may call + kcdb_credset_collect() to collect the credentials into the root + credential store. + + The user interface will only display credentials that are in the + root credential store. No notifications are generated for changes + to a non-root credential set. + + Use kcdb_credset_delete() to delete the credential set once it is + created. + + \see kcdb_credset_delete() + \see kcdb_credset_collect() +*/ +KHMEXP khm_int32 KHMAPI +kcdb_credset_create(khm_handle * result); + +/** \brief Delete a credential set + + \see kcdb_credset_create() +*/ +KHMEXP khm_int32 KHMAPI +kcdb_credset_delete(khm_handle credset); + +/** \brief Collect credentials from a credential set to another credential set. + + Collecting a subset of credentials from credential set \a cs_src + into credential set \a cs_dest involves the following steps: + + - Select all credentials from \a cs_src that matches the \a + identity and \a type specified in the function call and add them + to the \a cs_dest credential set if they are not there already. + Note that if neither credential set is not the root credential + store, then the credentials will be added by reference, while if + it is the root credential store, the credentials will be + duplicated, and the copies will be added to \a cs_dest. + + - If a selected credential in \a cs_src already exists in \a + cs_dest, then update the credential in \a cs_dest with the + credential fields in \a cs_src. In other words, once a + credential is found to exist in both \a cs_src and \a cs_dest, + all the non-null fields from the credential in \a cs_src will be + copied to the credential in \a cs_dest. Fields which are null + (undefined) in \a cs_src and are non-null in \a cs_dest will be + left unmodified in \a cs_dest. + + One notable exception is the credentials' flags. All flags in + \a cs_src which are not included in + ::KCDB_CRED_FLAGMASK_ADDITIVE will be copied to the + corresponding bits in the flags of \a cs_dest. However, flags + that are included in ::KCDB_CRED_FLAGMASK_ADDITIVE will be added + to the corresponding bits in \a cs_dest. + + (See notes below) + + - Remove all credentials from \a cs_dest that match the \a + identity and \a type that do not appear in \a cs_src. (see notes + below) + + For performance reasons, plugins should use kcdb_credset_collect() + to update the root credentials store instead of adding and + removing individual credentials from the root store. + + Only credentials that are associated with active identities are + affected by kcdb_credset_collect(). + + \param[in] cs_dest A handle to the destination credential set. If + this is \a NULL, then it is assumed to refer to the root + credential store. + + \param[in] cs_src A handle to the source credential set. If this + is NULL, then it is assumed to refer to the root credential + store. + + \param[in] identity A handle to an identity. Setting this to NULL + collects all identities in the credential set. + + \param[in] type A credentials type. Setting this to + KCDB_CREDTYPE_ALL collects all credential types in the set. + + \param[out] delta A bit mask that indicates the modifications that + were made to \a cs_dest as a result of the collect operation. + This is a combination of KCDB_DELTA_* values. This parameter + can be \a NULL if the value is not required. + + \warning If \a identity and \a type is set to a wildcard, all + credentials in the root store that are not in this credentials + set will be deleted. + + \note Two credentials \a A and \a B are considered equal if: + - They refer to the same identity + - Both have the same credential type + - Both have the same name + + \note This is the only supported way of modifying the root + credential store. + + \note \a cs_src and \a cs_dest can not refer to the same + credentials set. + + \note The destination credential set cannot be sealed. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_credset_collect(khm_handle cs_dest, + khm_handle cs_src, + khm_handle identity, + khm_int32 type, + khm_int32 * delta); + +/*! \brief Credentials were added + \see kcdb_credset_collect() */ +#define KCDB_DELTA_ADD 1 + +/*! \brief Credentials were deleted + \see kcdb_credset_collect() */ +#define KCDB_DELTA_DEL 2 + +/*! \brief Credentials were modified + \see kcdb_credset_collect() */ +#define KCDB_DELTA_MODIFY 4 + +/*! \brief Indicates that the credential to be filtered is from the root store. + + \see kcdb_credset_collect_filtered() +*/ +#define KCDB_CREDCOLL_FILTER_ROOT 1 + +/*! \brief Indicates that the credential to be filtered is from the source + credential set + + \see kcdb_credset_collect_filtered() */ +#define KCDB_CREDCOLL_FILTER_SRC 2 + +/*! \brief Indicates that the credential to be filtered is from the destination + credential set + + \see kcdb_credset_collect_filtered() */ +#define KCDB_CREDCOLL_FILTER_DEST 4 + +/*! \brief Collect credentials from one credential set to another using a filter. + + Similar to kcdb_credset_collect() except instead of selecting + credentials by matching against an identity and/or type, a filter + function is called. If the filter function returns non-zero for a + credential, that credential is selected. + + Credentials in the source and destination credential sets are + passed into the filter function. Depending on whether the + credential is in the source credential set or destination + credential set, the \a flag parameter may have either \a + KCDB_CREDCOLL_FILTER_SRC or \a KCDB_CREDCOLL_FILTER_DEST bits set. + Also, if either one of the credential sets is the root credential + store, then additionally \a KCDB_CREDCOLL_FILTER_ROOT would also + be set. + + See the kcdb_credset_collect() documentation for explanations of + the \a cs_src, \a cs_dest and \a delta parameters which perform + identical functions. + + \param[in] filter The filter of type ::kcdb_cred_filter_func + \param[in] rock A custom argument to be passed to the filter function. + + \see kcdb_credset_collect() +*/ +KHMEXP khm_int32 KHMAPI +kcdb_credset_collect_filtered(khm_handle cs_dest, + khm_handle cs_src, + kcdb_cred_filter_func filter, + void * rock, + khm_int32 * delta); + +/*! \brief Flush all credentials from a credential set + + Deletes all the crednetials from the credential set. + + \param[in] credset A handle to a credential set. Cannot be NULL. + + \note The credential set cannot be sealed +*/ +KHMEXP khm_int32 KHMAPI +kcdb_credset_flush(khm_handle credset); + +/*! \brief Extract credentials from one credential set to another + + Credentials from the source credential set are selected based on + the \a identity and \a type arguements. If a credential is + matched, then it is added to the \a destcredset. + + If the \a sourcecredset is the root credential set, the added + credentials are copies of the actual credentials in the root + credential set. Otherwise the credentials are references to the + original credentials in the \a sourcecredset . + + \param[in] destcredset Destination credential set. Must be valid. + + \param[in] sourcecredset The source credential set. If set to + NULL, extracts from the root credential set. + + \param[in] identity The identity to match in the source credential + set. If set to NULL, matches all identities. + + \param[in] type The credential type to match in the source credential set. + If set to KCDB_TYPE_INVALID, matches all types. + + \note This function does not check for duplicate credentials. + + \note The destination credential set cannot be sealed. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_credset_extract(khm_handle destcredset, + khm_handle sourcecredset, + khm_handle identity, + khm_int32 type); + +/*! \brief Extract credentials from one credential set to another using a filter. + + Similar to kcdb_credset_extract() except a filter function is used + to determine which credentials should be selected. + + \param[in] rock A custom argument to be passed in to the filter function. + + \note The destination credential set cannot be sealed. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_credset_extract_filtered(khm_handle destcredset, + khm_handle sourcecredset, + kcdb_cred_filter_func filter, + void * rock); + +/*! \brief Retrieve a held reference to a credential in a credential set based on index. + + \param[in] idx The index of the credential to retrieve. This is a + zero based index which goes from 0 ... (size of credset - 1). + + \param[out] cred The held reference to a credential. Call + kcdb_cred_release() to release the credential. + + \retval KHM_ERROR_SUCCESS Success. \a cred has a held reference to the credential. + \retval KHM_ERROR_OUT_OF_BOUNDS The index specified in \a idx is out of bounds. + \retval KHM_ERROR_DELETED The credential at index \a idx has been marked as deleted. + + \see kcdb_cred_release() +*/ +KHMEXP khm_int32 KHMAPI +kcdb_credset_get_cred(khm_handle credset, + khm_int32 idx, + khm_handle * cred); + +/*! \brief Search a credential set for a specific credential + + The credential set indicated by \a credset is searched for a + credential that satisfies the predicate function \a f. Each + credential starting at \a idx_start is passed into the predicate + function until it returns a non-zero value. At this point, that + credential is passed in to the \a cred parameter, and the index of + the credential is passed into the \a idx parameter. + + \param[in] credset The credential set to search on. Specify NULL + if you want to search teh root credential set. + + \param[in] idx_start The index at which to start the search after. + The first credential passed to the predicate function will be + at \a idx_start + 1. Specify -1 to start from the beginning + of the credential set. + + \param[in] f The predicate function. The \a flags parameter of + the predicate function will always receive 0. + + \param[in] rock An opaque parameter to be passed to the predicate + function \a f. + + \param[out] cred A held reference to the credential that satisfied + the predicate function or NULL if no such credential was + found. Note that if a valid credential is returned, the + calling function must release the credential using + kcdb_cred_release(). + + \param[out] idx The index of the credential passed in \a cred. + Specify NULL if the index is not required. + + \retval KHM_ERROR_SUCCESS A credential that satisfied the + predicate function was found and was assigned to \a cred. + + \retval KHM_ERROR_NOT_FOUND No credential was found that matched + the predicate function. + + \note When querying credential sets that are shared between + threads, it is possible that another thread modifies the + credential set between successive calls to + kcdb_credset_find_filtered(). Therefore a continued sequences of + searches are not guaranteed to exhastively cover the + credential set nor to not return duplicate matches. Duplicate + matches are possible if the order of the credentials in the + set was changed. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_credset_find_filtered(khm_handle credset, + khm_int32 idx_start, + kcdb_cred_filter_func f, + void * rock, + khm_handle * cred, + khm_int32 * idx); + +/*! \brief Find matching credential + + Searches a credential set for a credential that matches the + specified credential. For a credential to be a match, it must + have the same identity, credential type and name. + + \param[in] credset Credential set to search + + \param[in] cred_src Credetial to search on + + \param[out] cred_dest receieves the matching credential if the + search is successful. If a handle is returend, the + kcdb_cred_release() must be used to release the handle. If + the matching credential is not required, you can pass in NULL. + + \retval KHM_ERROR_SUCCESS The search was successful. A credential + was assigned to \a cred_dest + + \retval KHM_ERROR_NOT_FOUND A matching credential was not found. + */ +KHMEXP khm_int32 KHMAPI +kcdb_credset_find_cred(khm_handle credset, + khm_handle cred_src, + khm_handle *cred_dest); + + +/*! \brief Delete a credential from a credential set. + + The credential at index \a idx will be deleted. All the + credentials that are at indices \a idx + 1 and above will be moved + down to fill the gap and the size of the credential set will + decrease by one. + + Use kcdb_credset_del_cred_ref() to delete a credential by + reference. Using kcdb_credset_del_cred() is faster than + kcdb_credset_del_cred_ref(). + + If you call kcdb_credset_del_cred() or kcdb_credset_del_cred_ref() + from within kcdb_credset_apply(), the credential will only be + marked as deleted. They will not be removed. This means that the + size of the credential set will not decrease. To purge the + deleted credentials from the set, call kcdb_credset_purge() after + kcdb_credset_apply() completes. + + \note The credential set cannot be sealed. + + \see kcdb_credset_del_cred_ref() +*/ +KHMEXP khm_int32 KHMAPI +kcdb_credset_del_cred(khm_handle credset, + khm_int32 idx); + +/*! \brief Delete a credential from a credential set by reference. + + See kcdb_credset_del_cred() for description of what happens when a + credential is deleted from a credential set. + + \note The credential set cannot be sealed. + + \see kcdb_credset_del_cred() +*/ +KHMEXP khm_int32 KHMAPI +kcdb_credset_del_cred_ref(khm_handle credset, + khm_handle cred); + +/*! \brief Add a credential to a credential set. + + The credential is added by reference. In other words, no copy of + the credential is made. + + \param[in] idx Index of the new credential. This must be a value + in the range 0..(previous size of credential set) or -1. If + -1 is specifed, then the credential is appended at the end of + the set. + + \note The credential set cannot be sealed. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_credset_add_cred(khm_handle credset, + khm_handle cred, + khm_int32 idx); + +/*! \brief Get the number of credentials in a credential set. + + Credentials in a credential set may be volatile. When + kcdb_credeset_get_size() is called, the credential set is + compacted to only include credentials that are active at the time. + However, when you are iterating through the credential set, it + might be the case that some credentials would get marked as + deleted. These credentials will remain in the credential set + until the credential set is discarded or another call to + kcdb_credset_get_size() or kdcb_credset_purge() is made. + + If the credential set is sealed, then it will not be compacted and + will include deleted credentials as well. + + \see kcdb_credset_purge() + \see kcdb_credset_get_cred() +*/ +KHMEXP khm_int32 KHMAPI +kcdb_credset_get_size(khm_handle credset, + khm_size * size); + +/*! \brief Removes credentials that have been marked as deleted from a credential set. + + See description of \a kcdb_credset_purge() for a description of + what happens when credntials that are contained in a credential + set are deleted by an external entity. + + \note The credential set cannot be sealed. + + \see kcdb_credset_get_size() + \see kcdb_credset_get_cred() +*/ +KHMEXP khm_int32 KHMAPI +kcdb_credset_purge(khm_handle credset); + +/*! \brief Applies a function to all the credentials in a credentials set + + The given function is called for each credential in a credential + set. With each iteration, the function is called with a handle to + the credential and the user defined parameter \a rock. If the + function returns anything other than KHM_ERROR_SUCCESS, the + processing stops. + + \param[in] credset The credential set to apply the function to, or + NULL if you want to apply this to the root credential set. + + \param[in] f Function to call for each credential + + \param[in] rock An opaque parameter which is to be passed to 'f' + as the second argument. + + \retval KHM_ERROR_SUCCESS All the credentials were processed. + + \retval KHM_ERROR_EXIT The supplied function signalled the + processing to be aborted. + + \retval KHM_ERROR_INVALID_PARM One or more parameters were invalid. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_credset_apply(khm_handle credset, + kcdb_cred_apply_func f, + void * rock); + +/*! \brief Sort the contents of a credential set. + + \param[in] rock A custom argument to be passed in to the \a comp function. + + \note The credential set cannot be sealed. + + \see kcdb_cred_comp_generic() +*/ +KHMEXP khm_int32 KHMAPI +kcdb_credset_sort(khm_handle credset, + kcdb_cred_comp_func comp, + void * rock); + +/*! \brief Seal a credential set + + Sealing a credential set makes it read-only. To unseal a + credential set, call kcdb_credset_unseal(). + + Sealing is an additive operation. kcdb_credset_seal() can be + called muliple times. However, for every call to + kcdb_credset_seal() a call to kcdb_credset_unseal() must be made + to undo the seal. The credential set will become unsealed when + all the seals are released. + + Once sealed, the credential set will not allow any operation that + might change its contents. However, a selaed credential set can + still be delted. + + \see kcdb_credset_unseal() + */ +KHMEXP khm_int32 KHMAPI +kcdb_credset_seal(khm_handle credset); + +/*! \brief Unseal a credential set + + Undoes what kcdb_credset_seal() did. This does not guarantee that + the credential set is unsealed since there may be other seals. + + \see kcdb_credset_seal() + */ +KHMEXP khm_int32 KHMAPI +kcdb_credset_unseal(khm_handle credset); + +/*! \brief Defines a sort criterion for kcdb_cred_comp_generic() + + \see kcdb_cred_comp_generic() +*/ +typedef struct tag_kcdb_cred_comp_field { + khm_int32 attrib; /*!< a valid attribute ID */ + khm_int32 order; /*!< one of KCDB_CRED_COMP_INCREASING or + KCDB_CRED_COMP_DECREASING. Optionally, + KCDB_CRED_COMP_INITIAL_FIRST may be combined + with either. */ +} kcdb_cred_comp_field; + +/*! \brief Defines the sort order for a field in ::kcdb_cred_comp_field + + Sorts lexicographically ascending by string representation of field. +*/ +#define KCDB_CRED_COMP_INCREASING 0 + +/*! \brief Defines the sort order for a field in ::kcdb_cred_comp_field + + Sorts lexicographically descending by string representation of + field. + */ +#define KCDB_CRED_COMP_DECREASING 1 + +/*! \brief Defines the sort order for a field in ::kcdb_cred_comp_field + + Any credentials which have the ::KCDB_CRED_FLAG_INITIAL will be + grouped above any that don't. + + If that does not apply, then credentials from the primary + credentials type will be sorted before others. +*/ +#define KCDB_CRED_COMP_INITIAL_FIRST 2 + +/*! \brief Defines the sort criteria for kcdb_cred_comp_generic() + + \see kcdb_cred_comp_generic() +*/ +typedef struct tag_kcdb_cred_comp_order { + khm_int32 nFields; + kcdb_cred_comp_field * fields; +} kcdb_cred_comp_order; + +/*! \brief A generic compare function for comparing credentials. + + This function can be passed as a parameter to kcdb_credset_sort(). + + The \a rock parameter to this function should be a pointer to a + ::kcdb_cred_comp_order object. The \a fields member of the + ::kcdb_cred_comp_order object should point to an array of + ::kcdb_cred_comp_field objects, each of which specifies the sort + order in decreasing order of priority. The number of + ::kcdb_cred_comp_field objects in the array should correspond to + the \a nFields member in the ::kcdb_cred_comp_order object. + + The array of ::kcdb_cred_comp_field objects define the sort + criteria, in order. The \a attrib member should be a valid + attribute ID, while the \a order member determines whether the + sort order is increasing or decreasing. The exact meaning or + increasing or decreasing depends on the data type of the + attribute. + + \param[in] rock a pointer to a ::kcdb_cred_comp_order object +*/ +KHMEXP khm_int32 KHMAPI +kcdb_cred_comp_generic(khm_handle cred1, + khm_handle cred2, + void * rock); + +/*@}*/ + +/*! \defgroup kcdb_cred Credentials */ +/*@{*/ + +/*! \brief Maximum number of characters in a credential name */ +#define KCDB_CRED_MAXCCH_NAME 256 + +/*! \brief Maximum number of bytes in a credential name */ +#define KCDB_CRED_MAXCB_NAME (sizeof(wchar_t) * KCDB_CRED_MAXCCH_NAME) + +/*! \brief Marked as deleted */ +#define KCDB_CRED_FLAG_DELETED 0x00000008 + +/*! \brief Renewable */ +#define KCDB_CRED_FLAG_RENEWABLE 0x00000010 + +/*! \brief Initial + + Initial credentials form the basis of an identity. Some + properties of an initial credential, such as being renewable, are + directly inherited by the identity. An identity is also + automatically considered valid if it contains a valid initial + credential. + */ +#define KCDB_CRED_FLAG_INITIAL 0x00000020 + +/*! \brief Expired + + The credential's lifetime has ended. + */ +#define KCDB_CRED_FLAG_EXPIRED 0x00000040 + +/*! \brief Invalid + + The credential can no longer serve its intended function. This + may be because it is expired and is not renewable, or its + renewable time period has also expired, or for some other reason. + */ +#define KCDB_CRED_FLAG_INVALID 0x00000080 + +/*! \brief Credential is selected + + Indicates that the credential is selected. Note that using this + flag may be subject to race conditions. + */ +#define KCDB_CRED_FLAG_SELECTED 0x00000100 + +/*! \brief Bitmask indicating all known credential flags + */ +#define KCDB_CRED_FLAGMASK_ALL 0x0000ffff + +/*! \brief Bitmask indicating dditive flags + + Additive flags are special flags which are added to exiting + credentials based on new credentials when doing a collect + operation. See details on kcdb_credset_collect() + + \see kcdb_credset_collect() +*/ +#define KCDB_CRED_FLAGMASK_ADDITIVE KCDB_CRED_FLAG_SELECTED + +/*! \brief Generic credentials request + + This data structure is used as the format for a generic + credentials reqeust for a ::KMSG_KCDB_REQUEST message. A plugin + typically publishes this message so that a credentials provider + may handle it and in response, obtain the specified credential. + + While the \a identity, \a type and \a name members of the + structure are all optional, typically one would specify all three + or at least two for a credential provider to be able to provide + the credential unambigously. + + Credential providers do not need to respond to ::KMSG_KCDB_REQUEST + messages. However, if they do, they should make sure that they + are the only credential provider that is responding by setting the + \a semaphore member to a non-zero value. The \a semaphore is set + to zero when a request is initially sent out. When incrementing + the semaphore, the plugin should use a thread safe mechanism to + ensure that there are no race conditions that would allow more + than one provider to respond to the message. + */ +typedef struct tag_kcdb_cred_request { + khm_handle identity; /*!< Identity of the credential. Set + to NULL if not specified. */ + khm_int32 type; /*!< Type of the credential. Set to + KCDB_CREDTYPE_INVALID if not + specified. */ + wchar_t * name; /*!< Name of the credential. Set to + NULL if not specified. */ + + khm_handle dest_credset; /*!< If non-NULL, instructs whoever is + handling the request that the + credential thus obtained be placed + in this credential set in addition + to whereever it may place newly + acquired credentials. Note that + while this can be NULL if the new + credential does not need to be + placed in a credential set, it can + not equal the root credential + set. */ + + void * vparam; /*!< An unspecified + parameter. Specific credential types + may specify how this field is to be + used. */ + + long semaphore; /*!< Incremented by one when this + request is answered. Only one + credential provider is allowed to + answer a ::KMSG_KCDB_REQUEST + message. Initially, when the + message is sent out, this member + should be set to zero. */ +} kcdb_cred_request; + +/*! \brief Create a new credential + + \param[in] name Name of credential. \a name cannot be NULL and cannot + exceed \a KCDB_CRED_MAXCCH_NAME unicode characters including the + \a NULL terminator. + \param[in] identity A reference to an identity. + \param[in] cred_type A credentials type identifier for the credential. + \param[out] result Gets a held reference to the newly created credential. + Call kcdb_cred_release() or kcdb_cred_delete() to release the + reference. + \see kcdb_cred_release() +*/ +KHMEXP khm_int32 KHMAPI +kcdb_cred_create(wchar_t * name, + khm_handle identity, + khm_int32 cred_type, + khm_handle * result); + +/*! \brief Duplicate an existing credential. + + \param[out] newcred A held reference to the new credential if the call + succeeds. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_cred_dup(khm_handle cred, + khm_handle * newcred); + +/*! \brief Updates one credential using field values from another + + All fields that exist in \a vsrc will get copied to \a vdest and will + overwrite any values that are already there in \a vdest. However any + values that exist in \a vdest taht do not exist in \a vsrc will not be + modified. + + \retval KHM_ERROR_SUCCESS vdest was successfully updated + \retval KHM_ERROR_EQUIVALENT all fields in vsrc were present and equivalent in vdest +*/ +KHMEXP khm_int32 KHMAPI +kcdb_cred_update(khm_handle vdest, + khm_handle vsrc); + +/*! \brief Set an attribute in a credential by name + + \param[in] cbbuf Number of bytes of data in \a buffer. The + individual data type handlers may copy in less than this many + bytes in to the credential. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_cred_set_attrib(khm_handle cred, + wchar_t * name, + void * buffer, + khm_size cbbuf); + +/*! \brief Set an attribute in a credential by attribute id + + \param[in] buffer A pointer to a buffer containing the data to + assign to the attribute. Setting this to NULL has the effect + of removing any data that is already assigned to the + attribute. If \a buffer is non-NULL, then \a cbbuf should + specify the number of bytes in \a buffer. + + \param[in] cbbuf Number of bytes of data in \a buffer. The + individual data type handlers may copy in less than this many + bytes in to the credential. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_cred_set_attr(khm_handle cred, + khm_int32 attr_id, + void * buffer, + khm_size cbbuf); + +/*! \brief Get an attribute from a credential by name. + + \param[in] buffer The buffer that is to receive the attribute + value. Set this to NULL if only the required buffer size is + to be returned. + + \param[in,out] cbbuf The number of bytes available in \a buffer. + If \a buffer is not sufficient, returns KHM_ERROR_TOO_LONG and + sets this to the required buffer size. + + \note Set both \a buffer and \a cbbuf to NULL if only the + existence of the attribute is to be checked. If the attribute + exists in this credential then the function will return + KHM_ERROR_SUCCESS, otherwise it returns KHM_ERROR_NOT_FOUND. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_cred_get_attrib(khm_handle cred, + wchar_t * name, + khm_int32 * attr_type, + void * buffer, + khm_size * cbbuf); + +/*! \brief Get an attribute from a credential by attribute id. + + \param[in] buffer The buffer that is to receive the attribute + value. Set this to NULL if only the required buffer size is + to be returned. + + \param[in,out] cbbuf The number of bytes available in \a buffer. + If \a buffer is not sufficient, returns KHM_ERROR_TOO_LONG and + sets this to the required buffer size. + + \param[out] attr_type Receives the data type of the attribute. + Set this to NULL if the type is not required. + + \note Set both \a buffer and \a cbbuf to NULL if only the + existence of the attribute is to be checked. If the attribute + exists in this credential then the function will return + KHM_ERROR_SUCCESS, otherwise it returns KHM_ERROR_NOT_FOUND. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_cred_get_attr(khm_handle cred, + khm_int32 attr_id, + khm_int32 * attr_type, + void * buffer, + khm_size * cbbuf); + +/*! \brief Get the name of a credential. + + \param[in] buffer The buffer that is to receive the credential + name. Set this to NULL if only the required buffer size is to + be returned. + + \param[in,out] cbbuf The number of bytes available in \a buffer. + If \a buffer is not sufficient, returns KHM_ERROR_TOO_LONG and + sets this to the required buffer size. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_cred_get_name(khm_handle cred, + wchar_t * buffer, + khm_size * cbbuf); + +/*! \brief Get the string representation of a credential attribute. + + A shortcut function which generates the string representation of a + credential attribute directly. + + \param[in] vcred A handle to a credential + + \param[in] attr_id The attribute to retrieve + + \param[out] buffer A pointer to a string buffer which receives the + string form of the attribute. Set this to NULL if you only + want to determine the size of the required buffer. + + \param[in,out] pcbbuf A pointer to a #khm_int32 that, on entry, + holds the size of the buffer pointed to by \a buffer, and on + exit, receives the actual number of bytes that were copied. + + \param[in] flags Flags for the string conversion. Can be set to + one of KCDB_TS_LONG or KCDB_TS_SHORT. The default is + KCDB_TS_LONG. + + \retval KHM_ERROR_SUCCESS Success + \retval KHM_ERROR_NOT_FOUND The given attribute was either invalid + or was not defined for this credential + \retval KHM_ERROR_INVALID_PARM One or more parameters were invalid + \retval KHM_ERROR_TOO_LONG Either \a buffer was NULL or the + supplied buffer was insufficient +*/ +KHMEXP khm_int32 KHMAPI +kcdb_cred_get_attr_string(khm_handle vcred, + khm_int32 attr_id, + wchar_t * buffer, + khm_size * pcbbuf, + khm_int32 flags); + +/*! \brief Get the string representation of a credential attribute by name. + + A shortcut function which generates the string representation of a + credential attribute directly. + + \param[in] vcred A handle to a credential + + \param[in] attrib The name of the attribute to retrieve + + \param[out] buffer A pointer to a string buffer which receives the + string form of the attribute. Set this to NULL if you only + want to determine the size of the required buffer. + + \param[in,out] pcbbuf A pointer to a #khm_int32 that, on entry, + holds the size of the buffer pointed to by \a buffer, and on + exit, receives the actual number of bytes that were copied. + + \param[in] flags Flags for the string conversion. Can be set to + one of KCDB_TS_LONG or KCDB_TS_SHORT. The default is + KCDB_TS_LONG. + + \see kcdb_cred_get_attr_string() +*/ +KHMEXP khm_int32 KHMAPI +kcdb_cred_get_attrib_string(khm_handle cred, + wchar_t * name, + wchar_t * buffer, + khm_size * cbbuf, + khm_int32 flags) ; + + +/*! \brief Get a held reference to the identity associated with a credential + + Use kcdb_identity_release() to release the reference that is + returned. + + \see kcdb_identity_relase() +*/ +KHMEXP khm_int32 KHMAPI +kcdb_cred_get_identity(khm_handle cred, + khm_handle * identity); + +/*! \brief Set the identity of a credential + + While it is ill-advised to change the identity of a credential + that has been placed in one or more credential sets, there can be + legitimate reasons for doing so. Only change the identity of a + credential that is not placed in a credential set or placed in a + credential set that is only used by a single entity. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_cred_set_identity(khm_handle vcred, + khm_handle id); + +/*! \brief Get the serial number for the credential. + + Each credential gets assigned a serial number at the time it is + created. This will stay with the credential for its lifetime. + + \param[out] pserial Receives the serial number. Cannot be NULL. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_cred_get_serial(khm_handle cred, + khm_ui_8 * pserial); + +/*! \brief Get the type of the credential. + + The returned type is a credential type. Doh. + + \param[out] type Receives the type. Cannot be NULL. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_cred_get_type(khm_handle cred, + khm_int32 * type); + +/*! \brief Retrieve flags from a credential + + The flags returned will be place in the location pointed to by \a + flags. Note that the specified credential must be an active + credential for the operation to succeed. This means the + ::KCDB_CRED_FLAG_DELETED will never be retured by this function. + */ +KHMEXP khm_int32 KHMAPI +kcdb_cred_get_flags(khm_handle cred, + khm_int32 * flags); + +/*! \brief Set the flags of a credential + + The flags specified in the \a mask parameter will be set to the + values specified in the \a flags parameter. The flags that are + not included in \a mask will not be modified. + + This function can not be used to set the ::KCDB_CRED_FLAG_DELETED + flag. If this bit is specified in either \a flags or \a mask, it + will be ignored. + + \see ::KCDB_CRED_FLAGMASK_ALL + */ +KHMEXP khm_int32 KHMAPI +kcdb_cred_set_flags(khm_handle cred, + khm_int32 flags, + khm_int32 mask); + +/*! \brief Hold a reference to a credential. + + Use kcdb_cred_release() to release the reference. + + \see kcdb_cred_release() +*/ +KHMEXP khm_int32 KHMAPI +kcdb_cred_hold(khm_handle cred); + +/*! \brief Release a held reference to a credential. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_cred_release(khm_handle cred); + +/*! \brief Delete a credential. + + The credential will be marked for deletion and will continue to + exist until all held references are released. If the credential + is bound to a credential set or the root credential store, it will + be removed from the respective container. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_cred_delete(khm_handle cred); + +/*! \brief Compare an attribute of two credentials by name. + + \return The return value is dependent on the type of the attribute + and indicate a weak ordering of the attribute values of the two + credentials. If one or both credentials do not contain the + attribute, the return value is 0, which signifies that no ordering + can be determined. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_creds_comp_attrib(khm_handle cred1, + khm_handle cred2, + wchar_t * name); + +/*! \brief Compare an attribute of two credentials by attribute id. + + \return The return value is dependent on the type of the attribute + and indicate a weak ordering of the attribute values of the two + credentials. If one or both credentials do not contain the + attribute, the return value is 0, which signifies that no ordering + can be determined. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_creds_comp_attr(khm_handle cred1, + khm_handle cred2, + khm_int32 attr_id); + +/*! \brief Compare two credentials for equivalence + + \return Non-zero if the two credentials are equal. Zero otherwise. + \note Two credentials are considered equal if all the following hold: + - Both refer to the same identity. + - Both have the same name. + - Both have the same type. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_creds_is_equal(khm_handle cred1, + khm_handle cred2); + +/*@}*/ +/*@}*/ + +/********************************************************************/ + +/*! \defgroup kcdb_type Credential attribute types */ +/*@{*/ + +/*! \brief Convert a field to a string + + Provides a string representation of a field in a credential. The + data buffer can be assumed to be valid. + + On entry, \a s_buf can be NULL if only the required size of the + buffer is to be returned. \a pcb_s_buf should be non-NULL and + should point to a valid variable of type ::khm_size that will, on + entry, contain the size of the buffer pointed to by \a s_buf if \a + s_buf is not \a NULL, and on exit will contain the number of bytes + consumed in \a s_buf, or the required size of the buffer if \a + s_buf was NULL or the size of the buffer was insufficient. + + The implementation should verify the parameters that are passed in + to the function. + + The data pointed to by \a data should not be modified in any way. + + \param[in] data Valid pointer to a block of data + + \param[in] cb_data Number of bytes in data block pointed to by \a + data + + \param[out] s_buf Buffer to receive the string representation of + data. If the data type flags has KCDB_TYPE_FLAG_CB_AUTO, then + this parameter could be set to KCDB_CBSIZE_AUTO. In this + case, the function should compute the size of the input buffer + assuming that the input buffer is valid. + + \param[in,out] pcb_s_buf On entry, contains the size of the buffer + pointed to by \a s_buf, and on exit, contains the number of + bytes used by the string representation of the data including + the NULL terminator + + \param[in] flags Flags for formatting the string + + \retval KHM_ERROR_SUCCESS The string representation of the data + field was successfully copied to \a s_buf and the size of the + buffer used was copied to \a pcb_s_buf. + + \retval KHM_ERROR_INVALID_PARM One or more parameters were invalid + + \retval KHM_ERROR_TOO_LONG Either \a s_buf was \a NULL or the size + indicated by \a pcb_s_buf was too small to contain the string + representation of the value. The required size of the buffer + is in \a pcb_s_buf. + + \note This documents the expected behavior of this prototype function + + \see ::kcdb_type + */ +typedef khm_int32 +(KHMAPI *kcdb_dtf_toString)(const void * data, + khm_size cb_data, + wchar_t * s_buf, + khm_size * pcb_s_buf, + khm_int32 flags); + +/*! \brief Verifies whetehr the given buffer contains valid data + + The function should examine the buffer and the size of the buffer + and determine whether or not the buffer contains valid data for + this data type. + + The data field pointed to by \a data should not be modified in any + way. + + \param[in] data A pointer to a data buffer + + \param[in] cb_data The number of bytes in the data buffer. If the + data type flags has KCDB_TYPE_FLAG_CB_AUTO, then this + parameter could be set to KCDB_CBSIZE_AUTO. In this case, the + function should compute the size of the input buffer assuming + that the input buffer is valid. + + \return TRUE if the data is valid, FALSE otherwise. + + \note This documents the expected behavior of this prototype function + + \see ::kcdb_type +*/ +typedef khm_boolean +(KHMAPI *kcdb_dtf_isValid)(const void * data, + khm_size cb_data); + +/*! \brief Compare two fields + + Compare the two data fields and return a value indicating their + relative ordering. The return value follows the same + specification as strcmp(). + + Both data buffers that are passed in can be assumed to be valid. + + None of the data buffers should be modified in any way. + + \param[in] data_l Valid pointer to first data buffer + + \param[in] cb_data_l Number of bytes in \a data_l. If the data + type flags has KCDB_TYPE_FLAG_CB_AUTO, then this parameter + could be set to KCDB_CBSIZE_AUTO. In this case, the function + should compute the size of the input buffer assuming that the + input buffer is valid. + + \param[in] data_r Valid pointer to second data buffer + + \param[in] cb_data_r Number of bytes in \a data_r. If the data + type flags has KCDB_TYPE_FLAG_CB_AUTO, then this parameter + could be set to KCDB_CBSIZE_AUTO. In this case, the function + should compute the size of the input buffer assuming that the + input buffer is valid. + + \return The return value should be + - Less than zero if \a data_l < \a data_r + - Equal to zero if \a data_l == \a data_r or if this data type can not be compared + - Greater than zero if \a data_l > \a data_r + + \note This documents the expected behavior of this prototype function + + \see ::kcdb_type +*/ +typedef khm_int32 +(KHMAPI *kcdb_dtf_comp)(const void * data_l, + khm_size cb_data_l, + const void * data_r, + khm_size cb_data_r); + +/*! \brief Duplicate a data field + + Duplicates a data field. The buffer pointed to by \a data_src + contains a valid field. The function should copy the field with + appropriate adjustments to \a data_dst. + + The \a data_dst parameter can be NULL if only the required size of + the buffer is needed. In this case, teh function should set \a + pcb_data_dst to the number of bytes required and then return + KHM_ERROR_TOO_LONG. + + \param[in] data_src Pointer to a valid data buffer + + \param[in] cb_data_src Number of bytes in \a data_src. If the data + type flags has KCDB_TYPE_FLAG_CB_AUTO, then this parameter + could be set to KCDB_CBSIZE_AUTO. In this case, the function + should compute the size of the input buffer assuming that the + input buffer is valid. + + \param[out] data_dst Poitner to destination buffer. Could be NULL + if only the required size of the destination buffer is to be + returned. + + \param[in,out] pcb_data_dst On entry specifies the number of bytes + in \a data_dst, and on exit should contain the number of bytes + copied. + + \retval KHM_ERROR_SUCCESS The data was successfully copied. The + number of bytes copied is in \a pcb_data_dst + + \retval KHM_ERROR_INVALID_PARM One or more parameters is incorrect. + + \retval KHM_ERROR_TOO_LONG Either \a data_dst was NULL or the size + of the buffer was insufficient. The required size is in \a + pcb_data_dst + + \note This documents the expected behavior of this prototype function + + \see ::kcdb_type + */ +typedef khm_int32 +(KHMAPI *kcdb_dtf_dup)(const void * data_src, + khm_size cb_data_src, + void * data_dst, + khm_size * pcb_data_dst); + +/*! \brief A data type descriptor. + + Handles basic operation for a specific data type. + + \see \ref cred_data_types +*/ +typedef struct tag_kcdb_type { + wchar_t * name; + khm_int32 id; + khm_int32 flags; + + khm_size cb_min; + khm_size cb_max; + + kcdb_dtf_toString toString; + /*!< Provides a string representation for a value. */ + + kcdb_dtf_isValid isValid; + /*!< Returns true of the value is valid for this data type */ + + kcdb_dtf_comp comp; + /*!< Compare two values and return \a strcmp style return value */ + + kcdb_dtf_dup dup; + /*!< Duplicate a value into a secondary buffer */ +} kcdb_type; + +/*! \name Flags for kcdb_type::toString +@{*/ +/*! \brief Specify that the short form of the string representation should be returned. + + Flags for #kcdb_type::toString. The flag specifies how long the + string representation should be. The specific length of a short + or long description is not restricted and it is up to the + implementation to choose how to interpret the flags. + + Usually, KCDB_TS_SHORT is specified when the amount of space that + is available to display the string is very restricted. It may be + the case that the string is truncated to facilitate displaying in + a constrainted space. +*/ +#define KCDB_TS_SHORT 1 + +/*! \brief Specify that the long form of the string representation should be returned + + Flags for #kcdb_type::toString. The flag specifies how long the + string representation should be. The specific length of a short + or long description is not restricted and it is up to the + implementation to choose how to interpret the flags. + +*/ +#define KCDB_TS_LONG 0 +/*@}*/ + +/*! \brief The maximum number of bytes allowed for a value of any type */ +#define KCDB_TYPE_MAXCB 16384 + +/*! \name Flags for kcdb_type +@{*/ + +/*! \brief The type supports KCDB_CBSIZE_AUTO. + + Used for types where the size of the object can be determined + through context or by the object content. Such as for objects + that have a fixed size or unicode strings that have a terminator. + + This implies that ALL the object manipulation callbacks that are + defined in this type definition support the KCDB_CBSIZE_AUTO + value. +*/ +#define KCDB_TYPE_FLAG_CB_AUTO 16 + +/*! \brief The \a cb_min member is valid. + + The \a cb_min member defines the minimum number of bytes that an + object of this type will consume. + + \note If this flag is used in conjunction with \a + KCDB_TYPE_FLAG_CB_MAX then, \a cb_min must be less than or equal + to \a cb_max. +*/ +#define KCDB_TYPE_FLAG_CB_MIN 128 + +/*! \brief The \a cb_max member is valid. + + The \a cb_max member defines the maximum number of bytes that an + object of this type will consume. + + \note If this flag is used in conjunction with \a + KCDB_TYPE_FLAG_CB_MIN then, \a cb_min must be less than or + equal to \a cb_max. */ +#define KCDB_TYPE_FLAG_CB_MAX 256 + +/*! \brief Denotes that objects of this type have a fixed size. + + If this flags is specified, then the type definition must also + specify cb_min and cb_max, which must both be the same value. + + \note Implies \a KCDB_TYPE_FLAG_CB_AUTO, \a KCDB_TYPE_FLAG_CB_MIN + and \a KCDB_TYPE_FLAG_CB_MAX. Pay special attention to the + implication of \a KCDB_TYPE_FLAG_AUTO. +*/ +#define KCDB_TYPE_FLAG_CB_FIXED (KCDB_TYPE_FLAG_CB_AUTO|KCDB_TYPE_FLAG_CB_MIN|KCDB_TYPE_FLAG_CB_MAX) + +/*@}*/ + +KHMEXP khm_int32 KHMAPI +kcdb_type_get_id(wchar_t *name, khm_int32 * id); + +/*! \brief Return the type descriptor for a given type id + + \param[out] info Receives a held reference to a type descriptor. + Use kcdb_type_release_info() to release the handle. If the \a + info parameter is NULL, the function returns KHM_ERROR_SUCCESS + if \a id is a valid type id, and returns KHM_ERROR_NOT_FOUND + otherwise. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_type_get_info(khm_int32 id, kcdb_type ** info); + +KHMEXP khm_int32 KHMAPI +kcdb_type_release_info(kcdb_type * info); + +KHMEXP khm_int32 KHMAPI +kcdb_type_get_name(khm_int32 id, + wchar_t * buffer, + khm_size * cbbuf); + +/*! \brief Register a credentials attribute type + + The credentials type record pointed to by \a type defines a new + credential attribute type. The \a id member of \a type may be set + to KCDB_TYPE_INVALID to indicate that an attribute ID is to be + generated automatically. + + \param[in] type The type descriptor + \param[out] new_id Receives the identifier for the credential attribute type. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_type_register(kcdb_type * type, + khm_int32 * new_id); + +/*! \brief Unregister a credential attribute type + + Removes the registration for the specified credentials attribute + type. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_type_unregister(khm_int32 id); + +KHMEXP khm_int32 KHMAPI +kcdb_type_get_next_free(khm_int32 * id); + +/*! \name Conversion functions +@{*/ +/*! \brief Convert a time_t value to FILETIME +*/ +KHMEXP void KHMAPI +TimetToFileTime( time_t t, LPFILETIME pft ); + +/*! \brief Convert a time_t interval to a FILETIME interval +*/ +KHMEXP void KHMAPI +TimetToFileTimeInterval(time_t t, LPFILETIME pft); + +/*! \brief Convert a FILETIME interval to seconds +*/ +KHMEXP long KHMAPI +FtIntervalToSeconds(LPFILETIME pft); + +/*! \brief Convert a FILETIME interval to milliseconds +*/ +KHMEXP long KHMAPI +FtIntervalToMilliseconds(LPFILETIME pft); + +/*! \brief Compare two FILETIME values + + The return value is similar to the return value of strcmp(), based + on the comparison of the two FILETIME values. + */ +KHMEXP long KHMAPI +FtCompare(LPFILETIME pft1, LPFILETIME pft2); + +/*! \brief Convert a FILETIME inverval to a string +*/ +KHMEXP khm_int32 KHMAPI +FtIntervalToString(LPFILETIME data, + wchar_t * buffer, + khm_size * cb_buf); + +/*! \brief Parse a string representing an interval into a FILETIME interval + + The string is a localized string which should look like the + following: + + \code + [number unit] [number unit]... + \endcode + + where \a number is an integer while \a unit is a localized + (possibly abbreviated) unit specification. The value of the + described interval is calculated as the sum of each \a number in + \a units. For example : + + \code + 1 hour 36 minutes + \endcode + + would result in an interval specification that's equivalent to 1 + hour and 36 minutes. Of course there is no restriction on the + order in which the \a number \a unit specifications are given and + the same unit may be repeated multiple times. + + \retval KHM_ERROR_INVALID_PARM The given string was invalid or had + a token that could not be parsed. It can also mean that \a + pft was NULL or \a str was NULL. + + \retval KHM_ERROR_SUCCESS The string was successfully parsed and + the result was placed in \a pft. +*/ +KHMEXP khm_int32 KHMAPI +IntervalStringToFt(FILETIME * pft, wchar_t * str); + +/*! \brief Return number of milliseconds till next representation change + + Returns the number of milliseconds that must elapse away from the + interval specified in pft \a for the representation of pft to change + from whatever it is right now. + + Returns 0 if the representation is not expected to change. +*/ +KHMEXP long KHMAPI +FtIntervalMsToRepChange(LPFILETIME pft); + +/*! \brief Convert a safe ANSI string to a Unicode string + + The resulting string is guaranteed to be NULL terminated and + within the size limit set by \a cbwstr. + + If the whole string cannot be converted, \a wstr is set to an + empty string. + + \return the number of characters converted. This is always either + the length of the string \a astr or 0. +*/ +KHMEXP int KHMAPI +AnsiStrToUnicode( wchar_t * wstr, size_t cbwstr, const char * astr); + +/*! \brief Convert a Unicode string to ANSI + + The resulting string is guaranteed to be NULL terminated and + within the size limit set by \a cbdest. + + \return the number of characters converted. This is always either + the length of the string \a src or 0. +*/ +KHMEXP int KHMAPI +UnicodeStrToAnsi( char * dest, size_t cbdest, const wchar_t * src); +/*@}*/ + +/*! \name Standard type identifiers and names +@{*/ + +/*! Maximum identifier number */ +#define KCDB_TYPE_MAX_ID 255 + +/*! \brief Invalid type + + Used by functions that return a type identifier to indicate that + the returned type identifier is invalid. Also used to indicate + that a type identifier is not available */ +#define KCDB_TYPE_INVALID (-1) + +/*! \brief All types + + Used by filters to indicate that all types are allowed. +*/ +#define KCDB_TYPE_ALL KCDB_TYPE_INVALID + +#define KCDB_TYPE_VOID 0 +#define KCDB_TYPE_STRING 1 +#define KCDB_TYPE_DATE 2 +#define KCDB_TYPE_INTERVAL 3 +#define KCDB_TYPE_INT32 4 +#define KCDB_TYPE_INT64 5 +#define KCDB_TYPE_DATA 6 + +#define KCDB_TYPENAME_VOID L"Void" +#define KCDB_TYPENAME_STRING L"String" +#define KCDB_TYPENAME_DATE L"Date" +#define KCDB_TYPENAME_INTERVAL L"Interval" +#define KCDB_TYPENAME_INT32 L"Int32" +#define KCDB_TYPENAME_INT64 L"Int64" +#define KCDB_TYPENAME_DATA L"Data" +/*@}*/ +/*@}*/ + +/********************************************************************/ + +/*! \defgroup kcdb_credattr Credential attributes */ +/*@{*/ + +/*! \brief Prototype callback function for computed data types. + + If the flags for a particular attribute specifies that the value + is computed, then a callback function should be specified. The + callback function will be called with a handle to a credential + along with the attribute ID for the requested attribute. The + function should place the computed value in \a buffer. The size + of the buffer in bytes is specifed in \a cbsize. However, if \a + buffer is \a NULL, then the required buffer size should be placed + in \a cbsize. + */ +typedef khm_int32 +(KHMAPI *kcdb_attrib_compute_cb)(khm_handle cred, + khm_int32 id, + void * buffer, + khm_size * cbsize); + +/*! \brief Credential attribute descriptor + + \see kcdb_attrib_register() +*/ +typedef struct tag_kcdb_attrib { + wchar_t * name; /*!< Name. (Not localized, + required) */ + khm_int32 id; /*!< Identifier. When registering, + this can be set to + ::KCDB_ATTR_INVALID if a unique + identifier is to be generated. */ + khm_int32 alt_id; /*!< Alternate identifier. If the \a + flags specify + ::KCDB_ATTR_FLAG_ALTVIEW, then this + field should specify the identifier + of the canonical attribute from + which this attribute is derived. */ + khm_int32 flags; /*!< Flags. Combination of \ref + kcdb_credattr_flags "attribute + flags" */ + khm_int32 type; /*!< Type of the attribute. Must be valid. */ + wchar_t * short_desc; /*!< Short description. (Localized, + optional) */ + wchar_t * long_desc; /*!< Long description. (Localized, + optional) */ + + kcdb_attrib_compute_cb compute_cb; + /*!< Callback. Required if \a flags + specify ::KCDB_ATTR_FLAG_COMPUTED. */ + khm_size compute_min_cbsize; + /*!< Minimum number of bytes required + to store this attribute. Required + if ::KCDB_ATTR_FLAG_COMPUTED is + specified.*/ + khm_size compute_max_cbsize; + /*!< Maximum number of bytes required + to store this attribute. Required + if ::KCDB_ATTR_FLAG_COMPUTED is + specified.*/ +} kcdb_attrib; + +/*! \brief Retrieve the ID of a named attribute */ +KHMEXP khm_int32 KHMAPI +kcdb_attrib_get_id(wchar_t *name, + khm_int32 * id); + +/*! \brief Register an attribute + + \param[out] new_id Receives the ID of the newly registered + attribute. If the \a id member of the ::kcdb_attrib object is + set to KCDB_ATTR_INVALID, then a unique ID is generated. */ +KHMEXP khm_int32 KHMAPI +kcdb_attrib_register(kcdb_attrib * attrib, + khm_int32 * new_id); + +/*! \brief Retrieve the attribute descriptor for an attribute + + The descriptor that is returned must be released through a call to + kcdb_attrib_release_info() + + If only the validity of the attribute identifier needs to be + checked, you can pass in NULL for \a attrib. In this case, if the + identifier is valid, then the funciton will return + KHM_ERROR_SUCCESS, otherwise it will return KHM_ERROR_NOT_FOUND. + + \see kcdb_attrib_release_info() + */ +KHMEXP khm_int32 KHMAPI +kcdb_attrib_get_info(khm_int32 id, + kcdb_attrib ** attrib); + +/*! \brief Release an attribute descriptor + + \see kcdb_attrib_get_info() + */ +KHMEXP khm_int32 KHMAPI +kcdb_attrib_release_info(kcdb_attrib * attrib); + +/*! \brief Unregister an attribute + + Once an attribute ID has been unregistered, it may be reclaimed by + a subsequent call to kcdb_attrib_register(). +*/ +KHMEXP khm_int32 KHMAPI +kcdb_attrib_unregister(khm_int32 id); + +/*! \brief Retrieve the description of an attribute + + \param[in] flags Specify \a KCDB_TS_SHORT to retrieve the short description. */ +KHMEXP khm_int32 KHMAPI +kcdb_attrib_describe(khm_int32 id, + wchar_t * buffer, + khm_size * cbsize, + khm_int32 flags); + +/*! \brief Count attributes + + Counts the number of attributes that match the given criteria. + The criteria is specified against the flags of the attribute. An + attribute is a match if its flags satisfy the condition below: + + \code + (attrib.flags & and_flags) == (eq_flags & and_flags) + \endcode + + The number of attributes that match are returned in \a pcount. + */ +KHMEXP khm_int32 KHMAPI +kcdb_attrib_get_count(khm_int32 and_flags, + khm_int32 eq_flags, + khm_size * pcount); + +/*! \brief List attribute identifiers + + Lists the identifiers of the attributes that match the given + criteria. The criteria is specified against the flags of the + attribute. An attribute is a match if the following condition is + satisfied: + + \code + (attrib.flags & and_flags) == (eq_flags & and_flags) + \endcode + + The list of attributes found are copied to the \a khm_int32 array + specified in \a plist. The number of elements available in the + buffer \a plist is specified in \a pcsize. On exit, \a pcsize + will hold the actual number of attribute identifiers copied to the + array. + + \param[in] and_flags See above + \param[in] eq_flags See above + \param[in] plist A khm_int32 array + \param[in,out] pcsize On entry, holds the number of elements + available in the array pointed to by \a plist. On exit, holds + the number of elements copied to the array. + + \retval KHM_ERROR_SUCCESS The list of attribute identifiers have + been copied. + \retval KHM_ERROR_TOO_LONG The list was too long to fit in the + supplied buffer. As many elements as possible have been + copied to the \a plist array and the required number of + elements has been written to \a pcsize. + + \note The \a pcsize parameter specifies the number of khm_int32 + elements in the array and not the number of bytes in the + array. This is different from the usual size parameters used + in the NetIDMgr API. + */ +KHMEXP khm_int32 KHMAPI +kcdb_attrib_get_ids(khm_int32 and_flags, + khm_int32 eq_flags, + khm_int32 * plist, + khm_size * pcsize); + +/*! \defgroup kcdb_credattr_flags Attribute flags */ +/*@{*/ +/*! \brief The attribute is required */ +#define KCDB_ATTR_FLAG_REQUIRED 0x00000008 + +/*! \brief The attribute is computed. + + If this flag is set, the \a compute_cb, \a compute_min_cbsize and + \a compute_max_cbsize members of the ::kcdb_attrib attribute + descriptor must be assigned valid values. +*/ +#define KCDB_ATTR_FLAG_COMPUTED 0x00000010 + +/*! \brief System attribute. + + This cannot be specified for a custom attribute. Implies that the + value of the attribute is given by the credentials database + itself. +*/ +#define KCDB_ATTR_FLAG_SYSTEM 0x00000020 + +/*! \brief Hidden + + The attribute is not meant to be displayed to the user. Setting + this flag prevents this attribute from being listed in the list of + available data fields in the UI. +*/ +#define KCDB_ATTR_FLAG_HIDDEN 0x00000040 + +/*! \brief Property + + The attribute is a property. The main difference between regular + attributes and properties are that properties are not allocated + off the credentials record. Hence, a property can not be used as + a credentials field. Other objects such as identities can hold + property sets. A property set can hold both regular attributes as + well as properties. +*/ +#define KCDB_ATTR_FLAG_PROPERTY 0x00000080 + +/*! \brief Volatile + + A volatile property is one whose value changes often, such as + ::KCDB_ATTR_TIMELEFT. Some controls will make use of additional + logic to deal with such values, or not display them at all. + */ +#define KCDB_ATTR_FLAG_VOLATILE 0x00000100 + +/*! \brief Alternate view + + The attribute is actually an alternate representation of another + attribute. The Canonical attribute name is specified in \a + alt_id. + + Sometimes a certain attribute may need to be represented in + different ways. You can register multiple attributes for each + view. However, you should also provide a canonical attribute for + whenever the canonical set of attributes of the credential is + required. + */ +#define KCDB_ATTR_FLAG_ALTVIEW 0x00000200 +/*@}*/ + +/*! \defgroup kcdb_credattr_idnames Standard attribute IDs and names */ +/*@{*/ + +/*! \name Attribute related constants */ +/*@{*/ +/*! \brief Maximum valid attribute ID */ +#define KCDB_ATTR_MAX_ID 255 + +/*! \brief Minimum valid property ID */ +#define KCDB_ATTR_MIN_PROP_ID 4096 + +/*! \brief Maximum number of properties */ +#define KCDB_ATTR_MAX_PROPS 128 + +/*! \brief Maximum valid property ID */ +#define KCDB_ATTR_MAX_PROP_ID (KCDB_ATTR_MIN_PROP_ID + KCDB_ATTR_MAX_PROPS - 1) + +/*! \brief Invalid ID */ +#define KCDB_ATTR_INVALID (-1) + +/*! \brief First custom attribute ID */ +#define KCDB_ATTRID_USER 20 + +/*@}*/ + +/*!\name Attribute identifiers */ +/*@{*/ +/*! \brief Name of the credential + + - \b Type: STRING + - \b Flags: REQUIRED, COMPUTED, SYSTEM + */ +#define KCDB_ATTR_NAME 0 + +/*! \brief The identity handle for the credential + + - \b Type: INT64 + - \b Flags: REQUIRED, COMPUTED, SYSTEM, HIDDEN + + \note The handle returned in by specifying this attribute to + kcdb_cred_get_attr() or kcdb_cred_get_attrib() is not held. + While the identity is implicitly held for the duration that + the credential is held, it is not recommended to obtain a + handle to the identity using this method. Use + kcdb_cred_get_identity() instead. +*/ +#define KCDB_ATTR_ID 1 + +/*! \brief The name of the identity + + - \b Type: STRING + - \b Flags: REQUIRED, COMPUTED, SYSTEM + */ +#define KCDB_ATTR_ID_NAME 2 + +/*! \brief The type of the credential + + - \b Type: INT32 + - \b Flags: REQUIRED, COMPUTED, SYSTEM, HIDDEN +*/ +#define KCDB_ATTR_TYPE 3 + +/*! \brief Type name for the credential + + - \b Type: STRING + - \b Flags: REQUIRED, COMPUTED, SYSTEM +*/ +#define KCDB_ATTR_TYPE_NAME 4 + +/*! \brief Name of the parent credential + + - \b Type: STRING + - \b Flags: SYSTEM +*/ +#define KCDB_ATTR_PARENT_NAME 5 + +/*! \brief Issed on + + - \b Type: DATE + - \b Flags: SYSTEM +*/ +#define KCDB_ATTR_ISSUE 6 + +/*! \brief Expires on + + - \b Type: DATE + - \b Flags: SYSTEM +*/ +#define KCDB_ATTR_EXPIRE 7 + +/*! \brief Renewable period expires on + + - \b Type: DATE + - \b Flags: SYSTEM +*/ +#define KCDB_ATTR_RENEW_EXPIRE 8 + +/*! \brief Time left till expiration + + - \b Type: INTERVAL + - \b Flags: SYSTEM, COMPUTED, VOLATILE +*/ +#define KCDB_ATTR_TIMELEFT 9 + +#define KCDB_ATTR_RENEW_TIMELEFT 10 + +/*! \brief Location of the credential + + - \b Type: STRING + - \b Flags: SYSTEM +*/ +#define KCDB_ATTR_LOCATION 11 + +/*! \brief Lifetime of the credential + + - \b Type: INTERVAL + - \b Flags: SYSTEM +*/ +#define KCDB_ATTR_LIFETIME 12 + +#define KCDB_ATTR_RENEW_LIFETIME 13 + +/*! \brief Flags for the credential + + - \b Type: INT32 + - \b Flags: REQUIRED, COMPUTED, SYSTEM, HIDDEN + */ +#define KCDB_ATTR_FLAGS 14 + +/*@}*/ + +/*!\name Attribute names */ +/*@{ */ + +#define KCDB_ATTRNAME_NAME L"Name" +#define KCDB_ATTRNAME_ID L"Identity" +#define KCDB_ATTRNAME_ID_NAME L"IdentityName" +#define KCDB_ATTRNAME_TYPE L"TypeId" +#define KCDB_ATTRNAME_TYPE_NAME L"TypeName" +#define KCDB_ATTRNAME_FLAGS L"Flags" + +#define KCDB_ATTRNAME_PARENT_NAME L"Parent" +#define KCDB_ATTRNAME_ISSUE L"Issed" +#define KCDB_ATTRNAME_EXPIRE L"Expires" +#define KCDB_ATTRNAME_RENEW_EXPIRE L"RenewExpires" +#define KCDB_ATTRNAME_TIMELEFT L"TimeLeft" +#define KCDB_ATTRNAME_RENEW_TIMELEFT L"RenewTimeLeft" +#define KCDB_ATTRNAME_LOCATION L"Location" +#define KCDB_ATTRNAME_LIFETIME L"Lifetime" +#define KCDB_ATTRNAME_RENEW_LIFETIME L"RenewLifetime" + +/*@}*/ + +/*@}*/ + +/*@}*/ + +/*****************************************************************************/ + +/*! \defgroup kcdb_credtype Credential types */ +/*@{*/ + +/*! \brief Credential type descriptor */ +typedef struct tag_kcdb_credtype { + wchar_t * name; /*!< name (less than KCDB_MAXCB_NAME bytes) */ + khm_int32 id; + wchar_t * short_desc; /*!< short localized description (less + than KCDB_MAXCB_SHORT_DESC + bytes) */ + wchar_t * long_desc; /*!< long localized descriptionn (less + than KCDB_MAXCB_LONG_DESC + bytes) */ + khm_handle sub; /*!< Subscription for credentials type + hander. This should be a valid + subscription constructed through + a call to + kmq_create_subscription() and + must handle KMSG_CRED messages + that are marked as being sent to + type specific subscriptions. + + The subscription will be + automatically deleted with a call + to kmq_delete_subscription() when + the credentials type is + unregistered.*/ + +#ifdef _WIN32 + HICON icon; +#endif +} kcdb_credtype; + +/*! \brief Maximum value of a credential type identifier + + Credential type identifiers are assigned serially unless the + process registering the credential type sets a specific identity. + The maximum identifier number places a hard limit to the number of + credential types that can be registered at one time, which is + KCDB_CREDTYPE_MAX_ID + 1. + */ +#define KCDB_CREDTYPE_MAX_ID 31 + +/*! \brief Specify all credential types + + This value is used by functions which filter credentials based on + credential types. Specifying this value tells the filter to + accept all credential types. + */ +#define KCDB_CREDTYPE_ALL (-1) + +/*! \brief Automatically determine a credential type identifier + + Used with kcdb_credtype_register() to specify that the credential + type identifier should be automatically determined to avoid + collisions. + */ +#define KCDB_CREDTYPE_AUTO (-2) + +/*! \brief An invalid credential type + + Even though any non positive credential type ID is invalid + anywhere where a specific credential type ID is required, this + value is provided for explicit indication that the credential type + is invalid. Also it makes code more readable to have a constant + that shouts out INVALID. + +*/ +#define KCDB_CREDTYPE_INVALID (-3) + +/*! \brief Macro predicate for testing whether a credtype is valid + + Returns TRUE if the given credtype is valid. This is a safe + macro. +*/ +#define KCDB_CREDTYPE_IS_VALID(t) ((t) >= 0) + +/*! \brief Register a credentials type. + + The information given in the \a type parameter is used to register + a new credential type. Note that the \a name member of the \a + type should be unique among all credential types. + + You can specify ::KCDB_CREDTYPE_AUTO as the \a id member of \a + type to let kcdb_credtype_register() determine a suitable + credential type identifier. You can subsequently call + kcdb_credtype_get_id() to retrieve the generated id or pass a + valid pointer to a khm_int32 type variable as \a new_id. + + \param[in] type Credential type descriptor + + \param[out] new_id The credential type identifier that this type + was registered as. + + \retval KHM_ERROR_SUCCESS The credential type was successfully registered. + + \retval KHM_ERROR_INVALID_PARM One or more of the parameters were invalid + + \retval KHM_ERROR_TOO_LONG One or more of the string fields in \a + type exceeded the character limit for that field. + + \retval KHM_ERROR_NO_RESOURCES When autogenerating credential type + identifiers, this value indicates that the maximum number of + credential types have been registered. No more registrations + can be accepted unless some credentials type is unregisred. + + \retval KHM_ERROR_DUPLICATE The \a name or \a id that was + specified is already in use. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_credtype_register(kcdb_credtype * type, + khm_int32 * new_id); + +/*! \brief Return a held reference to a \a kcdb_credtype object describing the credential type. + + The reference points to a static internal object of type \a + kcdb_credtype. Use the kcdb_credtype_release_info() function to + release the reference. + + Also, the structure passed in as the \a type argument to + kcdb_credtype_register() is not valid as a credential type + descriptor. Use kcdb_credtype_get_info() to obtain the actual + credential type descriptor. + + \param[in] id Credentials type identifier. + + \param[out] type Receives the credentials descriptor handle. If + \a type is NULL, then no handle is returned. However, the + function will still return \a KHM_ERROR_SUCCESS if the \a id + parameter passed in is a valid credentials type identifier. + + \see kcdb_credtype_release_info() + \see kcdb_credtype_register() +*/ +KHMEXP khm_int32 KHMAPI +kcdb_credtype_get_info(khm_int32 id, + kcdb_credtype ** type); + +/*! \brief Release a reference to a \a kcdb_credtype object + + Undoes the hold obtained on a \a kcdb_credtype object from a + previous call to kcdb_credtype_get_info(). + + \see kcdb_credtype_get_info() + */ +KHMEXP khm_int32 KHMAPI +kcdb_credtype_release_info(kcdb_credtype * type); + +/*! \brief Unregister a credentials type + + Undoes the registration performed by kcdb_credtype_register(). + + This should only be done when the credentials provider is being + unloaded. + */ +KHMEXP khm_int32 KHMAPI +kcdb_credtype_unregister(khm_int32 id); + +/*! \brief Retrieve the name of a credentials type + + Given a credentials type identifier, retrieves the name. The name + is not localized and serves as a persistent identifier of the + credentials type. + + \param[out] buf The buffer to receive the name. Could be \a NULL + if only the length of the buffer is required. + + \param[in,out] cbbuf On entry, specifies the size of the buffer + pointed to by \a buf if \a buf is not NULL. On exit, contains + the number of bytes copied to \a buf or the required size of + the buffer. + + \retval KHM_ERROR_SUCCESS The call succeeded. + + \retval KHM_ERROR_TOO_LONG Either \a buf was NULL or the supplied + buffer was not large enough. The required size is in \a cbbuf. + + \retval KHM_ERROR_INVALID_PARM Invalid parameter. + */ +KHMEXP khm_int32 KHMAPI +kcdb_credtype_get_name(khm_int32 id, + wchar_t * buf, + khm_size * cbbuf); + +/*! \brief Retrieve the type specific subscription for a type + + Given a credentials type, this function returns the credentials + type specific subcription. It may return NULL if the subscription + is not available. + */ +KHMEXP khm_handle KHMAPI +kcdb_credtype_get_sub(khm_int32 id); + +/*! \brief Get the description of a credentials type + + Unlike the name of a credential type, the description is localized. + + \param[in] id Credentials type identifier + + \param[out] buf Receives the description. Can bet set to NULL if + only the size of the buffer is required. + + \param[in,out] cbbuf On entry, specifies the size of the buffer + pointed to by \a buf. On exit, specifies the required size of + the buffer or the number of bytes copied, depending on whether + the call succeeded or not. + + \param[in] flags Specify ::KCDB_TS_SHORT if the short version of + the description is desired if there is more than one. + + \retval KHM_ERROR_SUCCESS The call succeeded + \retval KHM_ERROR_TOO_LONG Either \a buf was NULL or the supplied buffer was insufficient. The required size is specified in \a cbbuf. + \retval KHM_ERROR_INVALID_PARM One or more parameters were invalid. + */ +KHMEXP khm_int32 KHMAPI +kcdb_credtype_describe(khm_int32 id, + wchar_t * buf, + khm_size * cbbuf, + khm_int32 flags); + +/*! \brief Look up the identifier of a credentials type by name + + Given a name, looks up the identifier. + + \param[in] name Name of the credentials type + \param[out] id Receives the identifier if the call succeeds + + */ +KHMEXP khm_int32 KHMAPI +kcdb_credtype_get_id(wchar_t * name, + khm_int32 * id); + +/*@}*/ + +/*********************************************************************/ + +/*! \defgroup kcdb_buf Generic access to buffer + + Currently, credentials and identities both hold record data types. + This set of API's allow an application to access fields in the + records using a single interface. Note that credentials only + accept regular attributes while identities can hold both + attributes and properties. + + Handles to credentials and identities are implicitly also handles + to records. Thus they can be directly used as such. +*/ +/*@{*/ + +/*! \brief Get an attribute from a record by attribute id. + + \param[in] buffer The buffer that is to receive the attribute + value. Set this to NULL if only the required buffer size is + to be returned. + + \param[in,out] cbbuf The number of bytes available in \a buffer. + If \a buffer is not sufficient, returns KHM_ERROR_TOO_LONG and + sets this to the required buffer size. + + \param[out] attr_type Receives the data type of the attribute. + Set this to NULL if the type is not required. + + \note Set both \a buffer and \a cbbuf to NULL if only the + existence of the attribute is to be checked. If the attribute + exists in this record then the function will return + KHM_ERROR_SUCCESS, otherwise it returns KHM_ERROR_NOT_FOUND. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_buf_get_attr(khm_handle record, + khm_int32 attr_id, + khm_int32 * attr_type, + void * buffer, + khm_size * pcb_buf); + +/*! \brief Get an attribute from a record by name. + + \param[in] buffer The buffer that is to receive the attribute + value. Set this to NULL if only the required buffer size is + to be returned. + + \param[in,out] cbbuf The number of bytes available in \a buffer. + If \a buffer is not sufficient, returns KHM_ERROR_TOO_LONG and + sets this to the required buffer size. + + \note Set both \a buffer and \a cbbuf to NULL if only the + existence of the attribute is to be checked. If the attribute + exists in this record then the function will return + KHM_ERROR_SUCCESS, otherwise it returns KHM_ERROR_NOT_FOUND. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_buf_get_attrib(khm_handle record, + wchar_t * attr_name, + khm_int32 * attr_type, + void * buffer, + khm_size * pcb_buf); + +/*! \brief Get the string representation of a record attribute. + + A shortcut function which generates the string representation of a + record attribute directly. + + \param[in] record A handle to a record + + \param[in] attr_id The attribute to retrieve + + \param[out] buffer A pointer to a string buffer which receives the + string form of the attribute. Set this to NULL if you only + want to determine the size of the required buffer. + + \param[in,out] pcbbuf A pointer to a #khm_int32 that, on entry, + holds the size of the buffer pointed to by \a buffer, and on + exit, receives the actual number of bytes that were copied. + + \param[in] flags Flags for the string conversion. Can be set to + one of KCDB_TS_LONG or KCDB_TS_SHORT. The default is + KCDB_TS_LONG. + + \retval KHM_ERROR_SUCCESS Success + \retval KHM_ERROR_NOT_FOUND The given attribute was either invalid + or was not defined for this record + \retval KHM_ERROR_INVALID_PARM One or more parameters were invalid + \retval KHM_ERROR_TOO_LONG Either \a buffer was NULL or the + supplied buffer was insufficient +*/ +KHMEXP khm_int32 KHMAPI +kcdb_buf_get_attr_string(khm_handle record, + khm_int32 attr_id, + wchar_t * buffer, + khm_size * pcbbuf, + khm_int32 flags); + +/*! \brief Get the string representation of a record attribute by name. + + A shortcut function which generates the string representation of a + record attribute directly. + + \param[in] record A handle to a record + + \param[in] attrib The name of the attribute to retrieve + + \param[out] buffer A pointer to a string buffer which receives the + string form of the attribute. Set this to NULL if you only + want to determine the size of the required buffer. + + \param[in,out] pcbbuf A pointer to a #khm_int32 that, on entry, + holds the size of the buffer pointed to by \a buffer, and on + exit, receives the actual number of bytes that were copied. + + \param[in] flags Flags for the string conversion. Can be set to + one of KCDB_TS_LONG or KCDB_TS_SHORT. The default is + KCDB_TS_LONG. + + \see kcdb_cred_get_attr_string() +*/ +KHMEXP khm_int32 KHMAPI +kcdb_buf_get_attrib_string(khm_handle record, + wchar_t * attr_name, + wchar_t * buffer, + khm_size * pcbbuf, + khm_int32 flags); + +/*! \brief Set an attribute in a record by attribute id + + \param[in] cbbuf Number of bytes of data in \a buffer. The + individual data type handlers may copy in less than this many + bytes in to the record. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_buf_set_attr(khm_handle record, + khm_int32 attr_id, + void * buffer, + khm_size cbbuf); + +/*! \brief Set an attribute in a record by name + + \param[in] cbbuf Number of bytes of data in \a buffer. The + individual data type handlers may copy in less than this many + bytes in to the record. +*/ +KHMEXP khm_int32 KHMAPI +kcdb_buf_set_attrib(khm_handle record, + wchar_t * attr_name, + void * buffer, + khm_size cbbuf); + +KHMEXP khm_int32 KHMAPI +kcdb_buf_hold(khm_handle record); + +KHMEXP khm_int32 KHMAPI +kcdb_buf_release(khm_handle record); + +/*@}*/ + +/********************************************************************/ + +/* Notification operation constants */ + +#define KCDB_OP_INSERT 1 +#define KCDB_OP_DELETE 2 +#define KCDB_OP_MODIFY 3 +#define KCDB_OP_ACTIVATE 4 +#define KCDB_OP_DEACTIVATE 5 +#define KCDB_OP_HIDE 6 +#define KCDB_OP_UNHIDE 7 +#define KCDB_OP_SETSEARCH 8 +#define KCDB_OP_UNSETSEARCH 9 +#define KCDB_OP_NEW_DEFAULT 10 + +/*@}*/ + +#endif diff --git a/src/windows/identity/kcreddb/kcreddbinternal.h b/src/windows/identity/kcreddb/kcreddbinternal.h new file mode 100644 index 000000000..699954cf0 --- /dev/null +++ b/src/windows/identity/kcreddb/kcreddbinternal.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KCREDDBINTERNAL_H__ +#define __KHIMAIRA_KCREDDBINTERNAL_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "buf.h" +#include "identity.h" +#include "attrib.h" +#include "type.h" +#include "credential.h" +#include "credset.h" +#include "credtype.h" + +/* globals */ + +extern HINSTANCE hinst_kcreddb; + +kconf_schema schema_kcdbconfig[]; + +void kcdb_init(void); +void kcdb_exit(void); +khm_handle kcdb_get_config(void); + +#endif \ No newline at end of file diff --git a/src/windows/identity/kcreddb/kcreddbmain.c b/src/windows/identity/kcreddb/kcreddbmain.c new file mode 100644 index 000000000..796084a99 --- /dev/null +++ b/src/windows/identity/kcreddb/kcreddbmain.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include + +HINSTANCE hinst_kcreddb; + +void +kcdb_process_attach(HINSTANCE hinstDLL) { + hinst_kcreddb = hinstDLL; + kcdb_init(); +} + +void +kcdb_process_detach(void) { + kcdb_exit(); +} diff --git a/src/windows/identity/kcreddb/lang/en_us/kcredres.rc b/src/windows/identity/kcreddb/lang/en_us/kcredres.rc new file mode 100644 index 000000000..2f733199a --- /dev/null +++ b/src/windows/identity/kcreddb/lang/en_us/kcredres.rc @@ -0,0 +1,130 @@ +// Microsoft Visual C++ generated resource script. +// +#include "..\..\langres.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "..\\..\\langres.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +1 TEXTINCLUDE +BEGIN + "..\\..\\langres.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_CREDDB "Khimaira Credentials Database" + IDS_NAME "Name" + IDS_IDENTITY "Identity" + IDS_ISSUED "Issued on" + IDS_EXPIRES "Expires on" + IDS_TIMELEFT "Time left" + IDS_LOCATION "Location" + IDS_PARENT "Parent" + IDS_TYPE "Type" + IDS_IVL_EXPIRED "(Expired)" + IDS_IVL_D_H "%I64u days %I64u hours" +END + +STRINGTABLE +BEGIN + IDS_IVL_H_M "%I64u hours %I64u mins" + IDS_IVL_M_S "%I64u mins %I64u secs" + IDS_IVL_S "%I64u seconds" + IDS_IVL_UNKNOWN "(Unknown)" + IDS_LIFETIME "Lifetime" + IDS_IVL_1D "1 day" + IDS_IVL_1H "1 hour" + IDS_IVL_1M "1 minute" + IDS_IVL_1S "1 second" + IDS_IVL_D "%I64u days" + IDS_IVL_H "%I64u hours" + IDS_IVL_M "%I64u minutes" + IDS_IVL_S_SPEC "s,sec,second,seconds,secs" + IDS_IVL_M_SPEC "m,min,mins,minutes" + IDS_IVL_H_SPEC "h,hrs,hours" + IDS_IVL_D_SPEC "d,day,days,ds" +END + +STRINGTABLE +BEGIN + IDS_IVl_W_SPEC "w,wk,wks,weeks" + IDS_FLAGS "Flags" + IDS_RENEW_TIMELEFT "Renewable Time left" + IDS_RENEW_EXPIRES "Renewable time expires" + IDS_RENEW_LIFETIME "Renewable lifetime" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/windows/identity/kcreddb/langres.h b/src/windows/identity/kcreddb/langres.h new file mode 100644 index 000000000..ab6620cd5 --- /dev/null +++ b/src/windows/identity/kcreddb/langres.h @@ -0,0 +1,49 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by D:\work\khimaira\src\kcreddb\lang\en_us\kcredres.rc +// +#define IDS_CREDDB 101 +#define IDS_NAME 102 +#define IDS_IDENTITY 103 +#define IDS_ISSUED 104 +#define IDS_EXPIRES 105 +#define IDS_TIMELEFT 106 +#define IDS_LOCATION 107 +#define IDS_PARENT 108 +#define IDS_TYPE 109 +#define IDS_IVL_EXPIRED 110 +#define IDS_IVL_D_H 111 +#define IDS_IVL_H_M 112 +#define IDS_IVL_M_S 113 +#define IDS_IVL_S 114 +#define IDS_IVL_UNKNOWN 115 +#define IDS_LIFETIME 116 +#define IDS_IVL_1D 117 +#define IDS_IVL_1H 118 +#define IDS_IVL_1M 119 +#define IDS_IVL_1S 120 +#define IDS_IVL_D 121 +#define IDS_IVL_H 122 +#define IDS_IVL_M 123 +#define IDS_IVL_S_SPEC 124 +#define IDS_IVL_M_SPEC 125 +#define IDS_IVL_H_SPEC 126 +#define IDS_IVL_D_SPEC 127 +#define IDS_IVl_W_SPEC 128 +#define IDS_IVL_W_SPEC 128 +#define IDS_IVl_W_SPEC 128 +#define IDS_FLAGS 129 +#define IDS_RENEW_TIMELEFT 130 +#define IDS_RENEW_EXPIRES 131 +#define IDS_RENEW_LIFETIME 132 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/windows/identity/kcreddb/resource.h b/src/windows/identity/kcreddb/resource.h new file mode 100644 index 000000000..dfb47e0d4 --- /dev/null +++ b/src/windows/identity/kcreddb/resource.h @@ -0,0 +1,27 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by kcreddb.rc +// +#define IDS_PROJNAME 100 +#define IDR_WMDMLOGGER 101 +#define IDS_LOG_SEV_INFO 201 +#define IDS_LOG_SEV_WARN 202 +#define IDS_LOG_SEV_ERROR 203 +#define IDS_LOG_DATETIME 204 +#define IDS_LOG_SRCNAME 205 +#define IDS_DEF_LOGFILE 301 +#define IDS_DEF_MAXSIZE 302 +#define IDS_DEF_SHRINKTOSIZE 303 +#define IDS_DEF_LOGENABLED 304 +#define IDS_MUTEX_TIMEOUT 401 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 201 +#define _APS_NEXT_COMMAND_VALUE 32768 +#define _APS_NEXT_CONTROL_VALUE 201 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/windows/identity/kcreddb/type.c b/src/windows/identity/kcreddb/type.c new file mode 100644 index 000000000..e41694544 --- /dev/null +++ b/src/windows/identity/kcreddb/type.c @@ -0,0 +1,1295 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include + +CRITICAL_SECTION cs_type; +hashtable * kcdb_type_namemap; +kcdb_type_i ** kcdb_type_tbl; +kcdb_type_i * kcdb_types = NULL; + +/* Void */ + +#define GENERIC_VOID_STR L"(Void)" + +khm_int32 KHMAPI kcdb_type_void_toString( + const void * d, + khm_size cbd, + wchar_t * buffer, + khm_size * cb_buf, + khm_int32 flags) +{ + size_t cbsize; + + if(!cb_buf) + return KHM_ERROR_INVALID_PARM; + + cbsize = sizeof(GENERIC_VOID_STR); + + if(!buffer || *cb_buf < cbsize) { + *cb_buf = cbsize; + return KHM_ERROR_TOO_LONG; + } + + StringCbCopy(buffer, *cb_buf, GENERIC_VOID_STR); + + *cb_buf = cbsize; + + return KHM_ERROR_SUCCESS; +} + +khm_boolean KHMAPI kcdb_type_void_isValid( + const void * d, + khm_size cbd) +{ + /* void is always valid, even if d is NULL */ + return TRUE; +} + +khm_int32 KHMAPI kcdb_type_void_comp( + const void * d1, + khm_size cbd1, + const void * d2, + khm_size cbd2) +{ + /* voids can not be compared */ + return 0; +} + +khm_int32 KHMAPI kcdb_type_void_dup( + const void * d_src, + khm_size cbd_src, + void * d_dst, + khm_size * cbd_dst) +{ + if(!cbd_dst) + return KHM_ERROR_INVALID_PARM; + + *cbd_dst = 0; + + /* copying a void doesn't do much */ + return KHM_ERROR_SUCCESS; +} + + +/* String */ +khm_int32 KHMAPI kcdb_type_string_toString( + const void * d, + khm_size cbd, + wchar_t * buffer, + khm_size * cb_buf, + khm_int32 flags) +{ + size_t cbsize; + wchar_t * sd; + + if(!cb_buf) + return KHM_ERROR_INVALID_PARM; + + sd = (wchar_t *) d; + + if(FAILED(StringCbLength(sd, KCDB_TYPE_MAXCB, &cbsize))) + return KHM_ERROR_INVALID_PARM; + + cbsize += sizeof(wchar_t); + + if(!buffer || *cb_buf < cbsize) { + *cb_buf = cbsize; + return KHM_ERROR_TOO_LONG; + } + + StringCbCopy(buffer, *cb_buf, sd); + + *cb_buf = cbsize; + + return KHM_ERROR_SUCCESS; +} + +khm_boolean KHMAPI kcdb_type_string_isValid( + const void * d, + khm_size cbd) +{ + size_t cbsize; + + if(cbd == KCDB_CBSIZE_AUTO) + cbd = KCDB_TYPE_MAXCB; + + if(FAILED(StringCbLength((wchar_t *) d, cbd, &cbsize))) + return FALSE; + else + return TRUE; +} + +khm_int32 KHMAPI kcdb_type_string_comp( + const void * d1, + khm_size cbd1, + const void * d2, + khm_size cbd2) +{ + return wcscmp((const wchar_t *) d1, (const wchar_t *) d2); +} + +khm_int32 KHMAPI kcdb_type_string_dup( + const void * d_src, + khm_size cbd_src, + void * d_dst, + khm_size * cbd_dst) +{ + size_t cbsize; + + if(!cbd_dst) + return KHM_ERROR_INVALID_PARM; + + if(cbd_src == KCDB_CBSIZE_AUTO) { + cbd_src = KCDB_TYPE_MAXCB; + } + + if(FAILED(StringCbLength((const wchar_t *) d_src, cbd_src, &cbsize))) { + return KHM_ERROR_UNKNOWN; + } + + cbsize += sizeof(wchar_t); + + if(!d_dst || *cbd_dst < cbsize) { + *cbd_dst = cbsize; + return KHM_ERROR_TOO_LONG; + } + + StringCbCopy((wchar_t *) d_dst, *cbd_dst, (const wchar_t *) d_src); + *cbd_dst = cbsize; + + return KHM_ERROR_SUCCESS; +} + +/* Date and time */ + + +khm_int32 KHMAPI kcdb_type_date_toString( + const void * d, + khm_size cbd, + wchar_t * buffer, + khm_size * cb_buf, + khm_int32 flags) +{ + size_t cbsize; + size_t cchsize; + wchar_t * bufend; + SYSTEMTIME st_now; + SYSTEMTIME st_d; + SYSTEMTIME st_dl; + FILETIME *ft; + int today = 0; + + if(!cb_buf) + return KHM_ERROR_INVALID_PARM; + + ft = (FILETIME *) d; + + GetLocalTime(&st_now); + FileTimeToSystemTime(ft, &st_d); + SystemTimeToTzSpecificLocalTime(NULL, &st_d, &st_dl); + if(st_now.wYear == st_dl.wYear && + st_now.wMonth == st_dl.wMonth && + st_now.wDay == st_dl.wDay) + today = 1; + + if(today && (flags & KCDB_TS_SHORT)) { + cbsize = 0; + } else { + cbsize = GetDateFormat( + LOCALE_USER_DEFAULT, + DATE_SHORTDATE, + &st_dl, + NULL, + NULL, + 0) * sizeof(wchar_t); + cbsize += sizeof(wchar_t); + } + + cbsize += GetTimeFormat( + LOCALE_USER_DEFAULT, + 0, + &st_dl, + NULL, + NULL, + 0) * sizeof(wchar_t); + + cbsize += sizeof(wchar_t); + + if(!buffer || *cb_buf < cbsize) { + *cb_buf = cbsize; + return KHM_ERROR_TOO_LONG; + } + + cchsize = cbsize / sizeof(wchar_t); + + if(!today || !(flags & KCDB_TS_SHORT)) { + size_t cch_buf_len; + + GetDateFormat( + LOCALE_USER_DEFAULT, + DATE_SHORTDATE, + &st_dl, + NULL, + buffer, + (int) cchsize); + + StringCchCat(buffer, cchsize, L" "); + + StringCchLength(buffer, cchsize, &cch_buf_len); + + bufend = buffer + cch_buf_len; + cchsize -= cch_buf_len; + } else { + bufend = buffer; + } + + GetTimeFormat( + LOCALE_USER_DEFAULT, + 0, + &st_dl, + NULL, + bufend, + (int) cchsize); + + *cb_buf = cbsize; + + return KHM_ERROR_SUCCESS; +} + +khm_boolean KHMAPI kcdb_type_date_isValid( + const void * d, + khm_size cbd) +{ + return (d && (cbd == KCDB_CBSIZE_AUTO || cbd == sizeof(FILETIME))); +} + +khm_int32 KHMAPI kcdb_type_date_comp( + const void * d1, + khm_size cbd1, + const void * d2, + khm_size cbd2) +{ + return (khm_int32) CompareFileTime((CONST FILETIME *) d1, (CONST FILETIME *) d2); +} + +khm_int32 KHMAPI kcdb_type_date_dup( + const void * d_src, + khm_size cbd_src, + void * d_dst, + khm_size * cbd_dst) +{ + if(d_dst && *cbd_dst >= sizeof(FILETIME)) { + *cbd_dst = sizeof(FILETIME); + *((FILETIME *) d_dst) = *((FILETIME *) d_src); + return KHM_ERROR_SUCCESS; + } else { + *cbd_dst = sizeof(FILETIME); + return KHM_ERROR_TOO_LONG; + } +} + +/* Interval */ + +/* returns the number of milliseconds that must elapse away from the + interval specified in pft for the representation of pft to change + from whatever it is right now */ +KHMEXP long KHMAPI FtIntervalMsToRepChange(LPFILETIME pft) +{ + __int64 ms,s,m,h,d; + long l; + + ms = *((__int64 *) pft) / 10000i64; + + if(ms < 0 || *((__int64 *) pft) == _I64_MAX) + return -1; + + s = ms / 1000i64; + m = s / 60; + h = s / 3600; + d = s / (3600*24); + + if(d > 0) { + /* rep change at next hour change */ + l = (long) (ms % (3600*1000i64)); + } else if(h > 0) { + /* rep change at next minute change */ + l = (long) (ms % (60*1000i64)); + } else { + l = (long) (ms % 1000); + } + + return l; +} + +KHMEXP khm_int32 KHMAPI FtIntervalToString(LPFILETIME data, wchar_t * buffer, khm_size * cb_buf) +{ + size_t cbsize; + __int64 s,m,h,d; + wchar_t ibuf[256]; + wchar_t fbuf[256]; + wchar_t * t; + + if(!cb_buf) + return KHM_ERROR_INVALID_PARM; + s = *((__int64 *) data) / 10000000i64; + + m = s / 60; + h = s / 3600; + d = s / (3600*24); + + if(*((__int64 *) data) == _I64_MAX) { + LoadString(hinst_kcreddb, IDS_IVL_UNKNOWN, ibuf, sizeof(ibuf)/sizeof(wchar_t)); + } else if(s < 0) { + LoadString(hinst_kcreddb, IDS_IVL_EXPIRED, ibuf, sizeof(ibuf)/sizeof(wchar_t)); + } else if(d > 0) { + h = (s - (d * 3600 * 24)) / 3600; + if(d == 1) { + LoadString(hinst_kcreddb, IDS_IVL_1D, ibuf, ARRAYLENGTH(ibuf)); + } else { + LoadString(hinst_kcreddb, IDS_IVL_D, fbuf, ARRAYLENGTH(fbuf)); + StringCbPrintf(ibuf, sizeof(ibuf), fbuf, d); + } + if(h > 0) { + StringCbCat(ibuf, sizeof(ibuf), L" "); + t = ibuf + wcslen(ibuf); + if(h == 1) + { + LoadString(hinst_kcreddb, IDS_IVL_1H, t, ARRAYLENGTH(ibuf) - wcslen(ibuf)); + } else { + LoadString(hinst_kcreddb, IDS_IVL_H, fbuf, ARRAYLENGTH(fbuf)); + StringCbPrintf(t, sizeof(ibuf) - wcslen(ibuf)*sizeof(wchar_t), fbuf, h); + } + } + } else if(h > 0) { + m = (s - (h * 3600)) / 60; + if(h == 1) { + LoadString(hinst_kcreddb, IDS_IVL_1H, ibuf, ARRAYLENGTH(ibuf)); + } else { + LoadString(hinst_kcreddb, IDS_IVL_H, fbuf, ARRAYLENGTH(fbuf)); + StringCbPrintf(ibuf, sizeof(ibuf), fbuf, h); + } + if(m > 0) { + StringCbCat(ibuf, sizeof(ibuf), L" "); + t = ibuf + wcslen(ibuf); + if(m == 1) + { + LoadString(hinst_kcreddb, IDS_IVL_1M, t, ARRAYLENGTH(ibuf) - wcslen(ibuf)); + } else { + LoadString(hinst_kcreddb, IDS_IVL_M, fbuf, ARRAYLENGTH(fbuf)); + StringCbPrintf(t, sizeof(ibuf) - wcslen(ibuf)*sizeof(wchar_t), fbuf, m); + } + } + } else if(m > 0) { + s -= m * 60; + if(m == 1) { + LoadString(hinst_kcreddb, IDS_IVL_1M, ibuf, ARRAYLENGTH(ibuf)); + } else { + LoadString(hinst_kcreddb, IDS_IVL_M, fbuf, ARRAYLENGTH(fbuf)); + StringCbPrintf(ibuf, sizeof(ibuf), fbuf, m); + } + if(s > 0) { + StringCbCat(ibuf, sizeof(ibuf), L" "); + t = ibuf + wcslen(ibuf); + if(s == 1) + { + LoadString(hinst_kcreddb, IDS_IVL_1S, t, ARRAYLENGTH(ibuf) - wcslen(ibuf)); + } else { + LoadString(hinst_kcreddb, IDS_IVL_S, fbuf, ARRAYLENGTH(fbuf)); + StringCbPrintf(t, sizeof(ibuf) - wcslen(ibuf)*sizeof(wchar_t), fbuf, s); + } + } + } else { + if(s == 1) { + LoadString(hinst_kcreddb, IDS_IVL_1S, ibuf, ARRAYLENGTH(ibuf)); + } else { + LoadString(hinst_kcreddb, IDS_IVL_S, fbuf, sizeof(fbuf)/sizeof(wchar_t)); + StringCbPrintf(ibuf, sizeof(ibuf), fbuf, s); + } + } + + StringCbLength(ibuf, sizeof(ibuf), &cbsize); + cbsize += sizeof(wchar_t); + + if(!buffer || *cb_buf < cbsize) { + *cb_buf = cbsize; + return KHM_ERROR_TOO_LONG; + } + + StringCbCopy(buffer, *cb_buf, ibuf); + *cb_buf = cbsize; + + return KHM_ERROR_SUCCESS; +} + +khm_int32 KHMAPI kcdb_type_interval_toString( + const void * data, + khm_size cbd, + wchar_t * buffer, + khm_size * cb_buf, + khm_int32 flags) +{ + return FtIntervalToString((LPFILETIME) data, buffer, cb_buf); +} + +khm_boolean KHMAPI kcdb_type_interval_isValid( + const void * d, + khm_size cbd) +{ + return (d && (cbd == sizeof(FILETIME) || cbd == KCDB_CBSIZE_AUTO)); +} + +khm_int32 KHMAPI kcdb_type_interval_comp( + const void * d1, + khm_size cbd1, + const void * d2, + khm_size cbd2) +{ + __int64 i1, i2; + + i1 = *((__int64 *) d1); + i2 = *((__int64 *) d2); + + if(i1 < i2) + return -1; + else if(i1 > i2) + return 1; + else + return 0; +} + +khm_int32 KHMAPI kcdb_type_interval_dup( + const void * d_src, + khm_size cbd_src, + void * d_dst, + khm_size * cbd_dst) +{ + if(d_dst && *cbd_dst >= sizeof(__int64)) { + *cbd_dst = sizeof(__int64); + *((__int64 *) d_dst) = *((__int64 *) d_src); + return KHM_ERROR_SUCCESS; + } else { + *cbd_dst = sizeof(__int64); + return KHM_ERROR_TOO_LONG; + } +} + +/* Int32 */ + +khm_int32 KHMAPI kcdb_type_int32_toString( + const void * d, + khm_size cbd, + wchar_t * buffer, + khm_size * cb_buf, + khm_int32 flags) +{ + size_t cbsize; + wchar_t ibuf[12]; + + if(!cb_buf) + return KHM_ERROR_INVALID_PARM; + + StringCbPrintf(ibuf, sizeof(ibuf), L"%d", *((khm_int32 *) d)); + StringCbLength(ibuf, sizeof(ibuf), &cbsize); + cbsize += sizeof(wchar_t); + + if(!buffer || *cb_buf < cbsize) { + *cb_buf = cbsize; + return KHM_ERROR_TOO_LONG; + } + + StringCbCopy((wchar_t *) buffer, *cb_buf, ibuf); + *cb_buf = cbsize; + + return KHM_ERROR_SUCCESS; +} + +khm_boolean KHMAPI kcdb_type_int32_isValid( + const void * d, + khm_size cbd) +{ + return (d && (cbd == KCDB_CBSIZE_AUTO || cbd == sizeof(khm_int32))); +} + +khm_int32 KHMAPI kcdb_type_int32_comp( + const void * d1, + khm_size cbd1, + const void * d2, + khm_size cbd2) +{ + return *((khm_int32 *) d1) - *((khm_int32 *) d2); +} + +khm_int32 KHMAPI kcdb_type_int32_dup( + const void * d_src, + khm_size cbd_src, + void * d_dst, + khm_size * cbd_dst) +{ + if(d_dst && (*cbd_dst >= sizeof(khm_int32))) { + *cbd_dst = sizeof(khm_int32); + *((khm_int32 *) d_dst) = *((khm_int32 *) d_src); + return KHM_ERROR_SUCCESS; + } else { + *cbd_dst = sizeof(khm_int32); + return KHM_ERROR_TOO_LONG; + } +} + +/* Int64 */ + +khm_int32 KHMAPI kcdb_type_int64_toString( + const void * d, + khm_size cbd, + wchar_t * buffer, + khm_size * cb_buf, + khm_int32 flags) +{ + size_t cbsize; + wchar_t ibuf[22]; + + if(!cb_buf) + return KHM_ERROR_INVALID_PARM; + + StringCbPrintf(ibuf, sizeof(ibuf), L"%I64d", *((__int64 *) d)); + StringCbLength(ibuf, sizeof(ibuf), &cbsize); + cbsize += sizeof(wchar_t); + + if(!buffer || *cb_buf < cbsize) { + *cb_buf = cbsize; + return KHM_ERROR_TOO_LONG; + } + + StringCbCopy((wchar_t *) buffer, *cb_buf, ibuf); + *cb_buf = cbsize; + + return KHM_ERROR_SUCCESS; +} + +khm_boolean KHMAPI kcdb_type_int64_isValid( + const void * d, + khm_size cbd) +{ + return (d && (cbd == KCDB_CBSIZE_AUTO || cbd == sizeof(__int64))); +} + +khm_int32 KHMAPI kcdb_type_int64_comp( + const void * d1, + khm_size cbd1, + const void * d2, + khm_size cbd2) +{ + __int64 r = *((__int64 *) d1) - *((__int64 *) d2); + return (r==0i64)?0:((r>0i64)?1:-1); +} + +khm_int32 KHMAPI kcdb_type_int64_dup( + const void * d_src, + khm_size cbd_src, + void * d_dst, + khm_size * cbd_dst) +{ + if(d_dst && (*cbd_dst >= sizeof(__int64))) { + *cbd_dst = sizeof(__int64); + *((__int64 *) d_dst) = *((__int64 *) d_src); + return KHM_ERROR_SUCCESS; + } else { + *cbd_dst = sizeof(__int64); + return KHM_ERROR_TOO_LONG; + } +} + +/* Data */ +#define GENERIC_DATA_STR L"(Data)" + +khm_int32 KHMAPI kcdb_type_data_toString( + const void * d, + khm_size cbd, + wchar_t * buffer, + khm_size * cb_buf, + khm_int32 flags) +{ + size_t cbsize; + + if(!cb_buf) + return KHM_ERROR_INVALID_PARM; + + cbsize = sizeof(GENERIC_DATA_STR); + + if(!buffer || *cb_buf < cbsize) { + *cb_buf = cbsize; + return KHM_ERROR_TOO_LONG; + } + + StringCbCopy(buffer, *cb_buf, GENERIC_DATA_STR); + + *cb_buf = cbsize; + + return KHM_ERROR_SUCCESS; +} + +khm_boolean KHMAPI kcdb_type_data_isValid( + const void * d, + khm_size cbd) +{ + /* data is always valid, even if d is NULL */ + return TRUE; +} + +khm_int32 KHMAPI kcdb_type_data_comp( + const void * d1, + khm_size cbd1, + const void * d2, + khm_size cbd2) +{ + /* datas can not be compared */ + return 0; +} + +khm_int32 KHMAPI kcdb_type_data_dup( + const void * d_src, + khm_size cbd_src, + void * d_dst, + khm_size * cbd_dst) +{ + if(!cbd_dst) + return KHM_ERROR_INVALID_PARM; + + *cbd_dst = cbd_src; + + if(!d_dst || *cbd_dst < cbd_src) { + return KHM_ERROR_TOO_LONG; + } else { + memcpy(d_dst, d_src, cbd_src); + return KHM_ERROR_SUCCESS; + } +} + + +void kcdb_type_msg_completion(kmq_message * m) +{ + kcdb_type_release((kcdb_type_i *) m->vparam); +} + +void kcdb_type_post_message(khm_int32 op, kcdb_type_i * t) +{ + kcdb_type_hold(t); + kmq_post_message(KMSG_KCDB, KMSG_KCDB_TYPE, op, (void *) t); +} + +void kcdb_type_init(void) +{ + kcdb_type type; + + InitializeCriticalSection(&cs_type); + kcdb_type_namemap = hash_new_hashtable( + KCDB_TYPE_HASH_SIZE, + hash_string, + hash_string_comp, + kcdb_type_add_ref, + kcdb_type_del_ref); + kcdb_type_tbl = malloc(sizeof(kcdb_type_i *) * (KCDB_TYPE_MAX_ID + 1)); + ZeroMemory(kcdb_type_tbl, sizeof(kcdb_type_i *) * (KCDB_TYPE_MAX_ID + 1)); + kcdb_types = NULL; + + /*TODO: register standard data types */ + + ZeroMemory(&type, sizeof(type)); + type.comp = kcdb_type_void_comp; + type.dup = kcdb_type_void_dup; + type.isValid = kcdb_type_void_isValid; + type.toString = kcdb_type_void_toString; + type.name = KCDB_TYPENAME_VOID; + type.id = KCDB_TYPE_VOID; + + kcdb_type_register(&type, NULL); + + ZeroMemory(&type, sizeof(type)); + type.comp = kcdb_type_string_comp; + type.dup = kcdb_type_string_dup; + type.isValid = kcdb_type_string_isValid; + type.toString = kcdb_type_string_toString; + type.name = KCDB_TYPENAME_STRING; + type.id = KCDB_TYPE_STRING; + type.flags = KCDB_TYPE_FLAG_CB_AUTO; + + kcdb_type_register(&type, NULL); + + ZeroMemory(&type, sizeof(type)); + type.comp = kcdb_type_date_comp; + type.dup = kcdb_type_date_dup; + type.isValid = kcdb_type_date_isValid; + type.toString = kcdb_type_date_toString; + type.name = KCDB_TYPENAME_DATE; + type.id = KCDB_TYPE_DATE; + type.cb_max = sizeof(FILETIME); + type.cb_min = sizeof(FILETIME); + type.flags = KCDB_TYPE_FLAG_CB_FIXED; + + kcdb_type_register(&type, NULL); + + ZeroMemory(&type, sizeof(type)); + type.comp = kcdb_type_interval_comp; + type.dup = kcdb_type_interval_dup; + type.isValid = kcdb_type_interval_isValid; + type.toString = kcdb_type_interval_toString; + type.name = KCDB_TYPENAME_INTERVAL; + type.id = KCDB_TYPE_INTERVAL; + type.cb_max = sizeof(__int64); + type.cb_min = sizeof(__int64); + type.flags = KCDB_TYPE_FLAG_CB_FIXED; + + kcdb_type_register(&type, NULL); + + ZeroMemory(&type, sizeof(type)); + type.comp = kcdb_type_int32_comp; + type.dup = kcdb_type_int32_dup; + type.isValid = kcdb_type_int32_isValid; + type.toString = kcdb_type_int32_toString; + type.name = KCDB_TYPENAME_INT32; + type.id = KCDB_TYPE_INT32; + type.cb_max = sizeof(khm_int32); + type.cb_min = sizeof(khm_int32); + type.flags = KCDB_TYPE_FLAG_CB_FIXED; + + kcdb_type_register(&type, NULL); + + ZeroMemory(&type, sizeof(type)); + type.comp = kcdb_type_int64_comp; + type.dup = kcdb_type_int64_dup; + type.isValid = kcdb_type_int64_isValid; + type.toString = kcdb_type_int64_toString; + type.name = KCDB_TYPENAME_INT64; + type.id = KCDB_TYPE_INT64; + type.cb_max = sizeof(__int64); + type.cb_min = sizeof(__int64); + type.flags = KCDB_TYPE_FLAG_CB_FIXED; + + kcdb_type_register(&type, NULL); + + ZeroMemory(&type, sizeof(type)); + type.comp = kcdb_type_data_comp; + type.dup = kcdb_type_data_dup; + type.isValid = kcdb_type_data_isValid; + type.toString = kcdb_type_data_toString; + type.name = KCDB_TYPENAME_DATA; + type.id = KCDB_TYPE_DATA; + + kcdb_type_register(&type, NULL); +} + +void kcdb_type_add_ref(const void *key, void *vt) +{ + kcdb_type_hold((kcdb_type_i *) vt); +} + +void kcdb_type_del_ref(const void *key, void *vt) +{ + kcdb_type_release((kcdb_type_i *) vt); +} + +khm_int32 kcdb_type_hold(kcdb_type_i * t) +{ + if(!t) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_type); + t->refcount++; + LeaveCriticalSection(&cs_type); + + return KHM_ERROR_SUCCESS; +} + +khm_int32 kcdb_type_release(kcdb_type_i * t) +{ + if(!t) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_type); + t->refcount--; + kcdb_type_check_and_delete(t->type.id); + LeaveCriticalSection(&cs_type); + + return KHM_ERROR_SUCCESS; +} + +void kcdb_type_exit(void) +{ + EnterCriticalSection(&cs_type); + free(kcdb_type_tbl); + /*TODO: free up the individual types */ + LeaveCriticalSection(&cs_type); + DeleteCriticalSection(&cs_type); +} + +void kcdb_type_check_and_delete(khm_int32 id) +{ + kcdb_type_i * t; + + if(id < 0 || id > KCDB_TYPE_MAX_ID) + return; + + EnterCriticalSection(&cs_type); + t = kcdb_type_tbl[id]; + if(t && !t->refcount) { + kcdb_type_tbl[id] = NULL; + LDELETE(&kcdb_types, t); + /* must already be out of the hash-table, otherwise refcount should not + be zero */ + free(t->type.name); + free(t); + } + LeaveCriticalSection(&cs_type); +} + +KHMEXP khm_int32 KHMAPI kcdb_type_get_id(wchar_t *name, khm_int32 * id) +{ + kcdb_type_i * t; + size_t cbsize; + + if(FAILED(StringCbLength(name, KCDB_MAXCB_NAME, &cbsize))) { + /* also fails of name is NULL */ + return KHM_ERROR_INVALID_PARM; + } + + EnterCriticalSection(&cs_type); + t = hash_lookup(kcdb_type_namemap, (void*) name); + LeaveCriticalSection(&cs_type); + + if(!t) { + *id = KCDB_TYPE_INVALID; + return KHM_ERROR_NOT_FOUND; + } else { + *id = t->type.id; + return KHM_ERROR_SUCCESS; + } +} + +KHMEXP khm_int32 KHMAPI kcdb_type_get_info(khm_int32 id, kcdb_type ** info) +{ + kcdb_type_i * t; + + if(id < 0 || id > KCDB_TYPE_MAX_ID) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_type); + t = kcdb_type_tbl[id]; + + if (t) + kcdb_type_hold(t); + LeaveCriticalSection(&cs_type); + + if(info) + *info = (kcdb_type *) t; + else if (t) + kcdb_type_release(t); + + return (t)? KHM_ERROR_SUCCESS : KHM_ERROR_NOT_FOUND; +} + +KHMEXP khm_int32 KHMAPI kcdb_type_release_info(kcdb_type * info) +{ + return kcdb_type_release((kcdb_type_i *) info); +} + +KHMEXP khm_int32 KHMAPI kcdb_type_get_name(khm_int32 id, wchar_t * buffer, khm_size * cbbuf) +{ + size_t cbsize; + kcdb_type_i * t; + + if(id < 0 || id > KCDB_TYPE_MAX_ID || !cbbuf) + return KHM_ERROR_INVALID_PARM; + + t = kcdb_type_tbl[id]; + + if(!t) + return KHM_ERROR_NOT_FOUND; + + if(FAILED(StringCbLength(t->type.name, KCDB_MAXCB_NAME, &cbsize))) + return KHM_ERROR_UNKNOWN; + + cbsize += sizeof(wchar_t); + + if(!buffer || *cbbuf < cbsize) { + *cbbuf = cbsize; + return KHM_ERROR_TOO_LONG; + } + + StringCbCopy(buffer, *cbbuf, t->type.name); + *cbbuf = cbsize; + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kcdb_type_register(kcdb_type * type, khm_int32 * new_id) +{ + kcdb_type_i *t; + size_t cbsize; + khm_int32 type_id; + + if(!type || + !type->comp || + !type->dup || + !type->isValid || + !type->toString || + !type->name) + return KHM_ERROR_INVALID_PARM; + + if((type->flags & KCDB_TYPE_FLAG_CB_MIN) && + (type->cb_min < 0 || type->cb_min > KCDB_TYPE_MAXCB)) + { + return KHM_ERROR_INVALID_PARM; + } + + if((type->flags & KCDB_TYPE_FLAG_CB_MAX) && + (type->cb_max < 0 || type->cb_max > KCDB_TYPE_MAXCB)) + { + return KHM_ERROR_INVALID_PARM; + } + + if((type->flags & KCDB_TYPE_FLAG_CB_MIN) && + (type->flags & KCDB_TYPE_FLAG_CB_MAX) && + (type->cb_max < type->cb_min)) + { + return KHM_ERROR_INVALID_PARM; + } + + if(FAILED(StringCbLength(type->name, KCDB_MAXCB_NAME, &cbsize))) + return KHM_ERROR_TOO_LONG; + + cbsize += sizeof(wchar_t); + + EnterCriticalSection(&cs_type); + if(type->id == KCDB_TYPE_INVALID) { + kcdb_type_get_next_free(&type_id); + } else if(type->id < 0 || type->id > KCDB_TYPE_MAX_ID) { + LeaveCriticalSection(&cs_type); + return KHM_ERROR_INVALID_PARM; + } else if(kcdb_type_tbl[type->id]) { + LeaveCriticalSection(&cs_type); + return KHM_ERROR_DUPLICATE; + } else { + type_id = type->id; + } + + if(type_id == KCDB_TYPE_INVALID) { + LeaveCriticalSection(&cs_type); + return KHM_ERROR_NO_RESOURCES; + } + + t = malloc(sizeof(kcdb_type_i)); + ZeroMemory(t, sizeof(kcdb_type_i)); + + t->type.name = malloc(cbsize); + StringCbCopy(t->type.name, cbsize, type->name); + + t->type.comp = type->comp; + t->type.dup = type->dup; + t->type.flags = type->flags; + t->type.id = type_id; + t->type.isValid = type->isValid; + t->type.toString = type->toString; + + LINIT(t); + + kcdb_type_tbl[type_id] = t; + LPUSH(&kcdb_types, t); + + hash_add(kcdb_type_namemap, (void *) t->type.name, (void *) t); + + LeaveCriticalSection(&cs_type); + + if(new_id) + *new_id = type_id; + + kcdb_type_post_message(KCDB_OP_INSERT, t); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kcdb_type_unregister(khm_int32 id) +{ + kcdb_type_i * t; + + if(id < 0 || id > KCDB_TYPE_MAX_ID) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_type); + t = kcdb_type_tbl[id]; + if(t) { + kcdb_type_post_message(KCDB_OP_DELETE, t); + /* we are going to remove t from the hash table. If no one is holding + a reference to it, then we can free it (actually, the del_ref code + will take care of that anyway). If there is a hold, then it will + get freed when they release it. + + Actually, the post_message call above pretty much guarantees that + the type has a hold on it.*/ + t->type.flags |= KCDB_TYPE_FLAG_DELETED; + hash_del(kcdb_type_namemap, t->type.name); + } + LeaveCriticalSection(&cs_type); + + if(t) + return KHM_ERROR_SUCCESS; + else + return KHM_ERROR_NOT_FOUND; +} + +KHMEXP khm_int32 KHMAPI kcdb_type_get_next_free(khm_int32 * id) +{ + int i; + + if(!id) + return KHM_ERROR_INVALID_PARM; + + /* do a linear search because this function only gets called a few times */ + EnterCriticalSection(&cs_type); + for(i=0; i <= KCDB_TYPE_MAX_ID; i++) { + if(!kcdb_type_tbl[i]) + break; + } + LeaveCriticalSection(&cs_type); + + if(i <= KCDB_TYPE_MAX_ID) { + *id = i; + return KHM_ERROR_SUCCESS; + } else { + *id = KCDB_TYPE_INVALID; + return KHM_ERROR_NO_RESOURCES; + } +} + +/* Conversion functions */ + +KHMEXP void KHMAPI TimetToFileTime( time_t t, LPFILETIME pft ) +{ + LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000i64; + pft->dwLowDateTime = (DWORD) ll; + pft->dwHighDateTime = (DWORD) (ll >> 32); +} + +KHMEXP void KHMAPI TimetToFileTimeInterval(time_t t, LPFILETIME pft) +{ + LONGLONG ll = Int32x32To64(t, 10000000); + pft->dwLowDateTime = (DWORD) ll; + pft->dwHighDateTime = (DWORD) (ll >> 32); +} + +KHMEXP long KHMAPI FtIntervalToSeconds(LPFILETIME pft) +{ + __int64 i = *((__int64 *) pft); + return (long) (i / 10000000i64); +} + +KHMEXP long KHMAPI FtIntervalToMilliseconds(LPFILETIME pft) +{ + __int64 i = *((__int64 *) pft); + return (long) (i / 10000i64); +} + +KHMEXP long KHMAPI FtCompare(LPFILETIME pft1, LPFILETIME pft2) { + __int64 i1 = *((__int64 *) pft1); + __int64 i2 = *((__int64 *) pft2); + + if (i1 < i2) + return -1; + if (i1 == i2) + return 0; + return 1; +} + +KHMEXP int KHMAPI AnsiStrToUnicode( wchar_t * wstr, size_t cbwstr, const char * astr) +{ + size_t nc; + + if(cbwstr == 0) + return 0; + + nc = strlen(astr); + if(nc == MultiByteToWideChar( + CP_ACP, + 0, + astr, + (int) nc, + wstr, + (int)(cbwstr / sizeof(wchar_t) - 1))) { + wstr[nc] = L'\0'; + } else { + wstr[0] = L'\0'; + nc = 0; + } + + return (int) nc; +} + +KHMEXP int KHMAPI UnicodeStrToAnsi( char * dest, size_t cbdest, const wchar_t * src) +{ + size_t nc; + + if(cbdest == 0) + return 0; + + dest[0] = 0; + + if(FAILED(StringCchLength(src, cbdest, &nc)) || nc*sizeof(char) >= cbdest) + // note that cbdest counts the terminating NULL, while nc doesn't + return 0; + + nc = WideCharToMultiByte( + CP_ACP, + WC_NO_BEST_FIT_CHARS, + src, + (int) nc, + dest, + (int) cbdest, + NULL, + NULL); + + dest[nc] = 0; + + return (int) nc; +} + +#define MAX_IVL_SPECLIST_LEN 256 +#define MAX_IVL_UNITS 5 + +enum _ivl_indices { + IVL_SECONDS = 0, + IVL_MINUTES, + IVL_HOURS, + IVL_DAYS, + IVL_WEEKS +}; + +typedef struct ivspec_t { + wchar_t str[MAX_IVL_SPECLIST_LEN]; + __int64 mul; +} ivspec; + +static ivspec ivspecs[MAX_IVL_UNITS]; +static BOOL ivspecs_loaded = FALSE; + +int _iv_is_in_spec(wchar_t *s, int n, wchar_t * spec) +{ + /* spec strigns are comma separated */ + wchar_t *b, *e; + + b = spec; + while(*b) { + e = wcschr(b, L','); + if(!e) + e = b + wcslen(b); + + if((e - b) == n && !wcsnicmp(b, s, n)) { + return TRUE; + } + + if(*e) + b = e+1; + else + break; + } + + return FALSE; +} + +KHMEXP khm_int32 KHMAPI IntervalStringToFt(FILETIME * pft, wchar_t * str) +{ + size_t cb; + wchar_t * b; + __int64 *pr, t; + + pr = (__int64 *) pft; + *pr = 0; + + /* ideally we should synchronize this, but it doesn't hurt if two + threads do this at the same time, because we only set the ivspecs_loaded + flag when we are done */ + if(!ivspecs_loaded) { + LoadString(hinst_kcreddb, IDS_IVL_S_SPEC, ivspecs[IVL_SECONDS].str, MAX_IVL_SPECLIST_LEN); + ivspecs[IVL_SECONDS].mul = 10000000i64; + LoadString(hinst_kcreddb, IDS_IVL_M_SPEC, ivspecs[IVL_MINUTES].str, MAX_IVL_SPECLIST_LEN); + ivspecs[IVL_MINUTES].mul = ivspecs[IVL_SECONDS].mul * 60; + LoadString(hinst_kcreddb, IDS_IVL_H_SPEC, ivspecs[2].str, MAX_IVL_SPECLIST_LEN); + ivspecs[IVL_HOURS].mul = ivspecs[IVL_MINUTES].mul * 60; + LoadString(hinst_kcreddb, IDS_IVL_D_SPEC, ivspecs[3].str, MAX_IVL_SPECLIST_LEN); + ivspecs[IVL_DAYS].mul = ivspecs[IVL_HOURS].mul * 24; + LoadString(hinst_kcreddb, IDS_IVL_W_SPEC, ivspecs[4].str, MAX_IVL_SPECLIST_LEN); + ivspecs[IVL_WEEKS].mul = ivspecs[IVL_DAYS].mul * 7; + + ivspecs_loaded = TRUE; + } + + if(!str || FAILED(StringCbLength(str, MAX_IVL_SPECLIST_LEN, &cb))) + return KHM_ERROR_INVALID_PARM; + + b = str; + t = 0; + while(*b) { + __int64 f = 1; + wchar_t *e; + int i; + + while(*b && iswspace(*b)) + b++; + + if(*b && iswdigit(*b)) { + f = _wtoi64(b); + + while(*b && iswdigit(*b)) + b++; + } + + while(*b && iswspace(*b)) + b++; + + if(!*b) /* no unit specified */ + return KHM_ERROR_INVALID_PARM; + + e = b; + + while(*e && !iswspace(*e)) + e++; + + for(i=0; i < MAX_IVL_UNITS; i++) { + if(_iv_is_in_spec(b, (int)(e-b), ivspecs[i].str)) + break; + } + + if(i==MAX_IVL_UNITS) + return KHM_ERROR_INVALID_PARM; + + t += f * ivspecs[i].mul; + + b = e; + } + + *pr = t; + + return KHM_ERROR_SUCCESS; +} diff --git a/src/windows/identity/kcreddb/type.h b/src/windows/identity/kcreddb/type.h new file mode 100644 index 000000000..9d8b52e25 --- /dev/null +++ b/src/windows/identity/kcreddb/type.h @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KCDB_TYPE_H +#define __KHIMAIRA_KCDB_TYPE_H + +/* Types */ + +typedef struct kcdb_type_i_t { + kcdb_type type; + + khm_int32 refcount; + + struct kcdb_type_i_t * next; + struct kcdb_type_i_t * prev; +} kcdb_type_i; + +#define KCDB_TYPE_HASH_SIZE 31 + +#define KCDB_TYPE_FLAG_DELETED 8 + +void kcdb_type_init(void); +void kcdb_type_exit(void); +void kcdb_type_add_ref(const void *key, void *vt); +void kcdb_type_del_ref(const void *key, void *vt); +void kcdb_type_msg_completion(kmq_message * m); +khm_int32 kcdb_type_hold(kcdb_type_i * t); +khm_int32 kcdb_type_release(kcdb_type_i * t); +void kcdb_type_check_and_delete(khm_int32 id); +void kcdb_type_post_message(khm_int32 op, kcdb_type_i * t); + +khm_int32 KHMAPI kcdb_type_void_toString( + const void * d, + khm_size cbd, + wchar_t * buffer, + khm_size * cb_buf, + khm_int32 flags); + +khm_boolean KHMAPI kcdb_type_void_isValid( + const void * d, + khm_size cbd); + +khm_int32 KHMAPI kcdb_type_void_comp( + const void * d1, + khm_size cbd1, + const void * d2, + khm_size cbd2); + +khm_int32 KHMAPI kcdb_type_void_dup( + const void * d_src, + khm_size cbd_src, + void * d_dst, + khm_size * cbd_dst); + +khm_int32 KHMAPI kcdb_type_string_toString( + const void * d, + khm_size cbd, + wchar_t * buffer, + khm_size * cb_buf, + khm_int32 flags); + +khm_boolean KHMAPI kcdb_type_string_isValid( + const void * d, + khm_size cbd); + +khm_int32 KHMAPI kcdb_type_string_comp( + const void * d1, + khm_size cbd1, + const void * d2, + khm_size cbd2); + +khm_int32 KHMAPI kcdb_type_string_dup( + const void * d_src, + khm_size cbd_src, + void * d_dst, + khm_size * cbd_dst); + +khm_int32 KHMAPI kcdb_type_date_toString( + const void * d, + khm_size cbd, + wchar_t * buffer, + khm_size * cb_buf, + khm_int32 flags); + +khm_boolean KHMAPI kcdb_type_date_isValid( + const void * d, + khm_size cbd); + +khm_int32 KHMAPI kcdb_type_date_comp( + const void * d1, + khm_size cbd1, + const void * d2, + khm_size cbd2); + +khm_int32 KHMAPI kcdb_type_date_dup( + const void * d_src, + khm_size cbd_src, + void * d_dst, + khm_size * cbd_dst); + +khm_int32 KHMAPI kcdb_type_interval_toString( + const void * d, + khm_size cbd, + wchar_t * buffer, + khm_size * cb_buf, + khm_int32 flags); + +khm_boolean KHMAPI kcdb_type_interval_isValid( + const void * d, + khm_size cbd); + +khm_int32 KHMAPI kcdb_type_interval_comp( + const void * d1, + khm_size cbd1, + const void * d2, + khm_size cbd2); + +khm_int32 KHMAPI kcdb_type_interval_dup( + const void * d_src, + khm_size cbd_src, + void * d_dst, + khm_size * cbd_dst); + +khm_int32 KHMAPI kcdb_type_int32_toString( + const void * d, + khm_size cbd, + wchar_t * buffer, + khm_size * cb_buf, + khm_int32 flags); + +khm_boolean KHMAPI kcdb_type_int32_isValid( + const void * d, + khm_size cbd); + +khm_int32 KHMAPI kcdb_type_int32_comp( + const void * d1, + khm_size cbd1, + const void * d2, + khm_size cbd2); + +khm_int32 KHMAPI kcdb_type_int32_dup( + const void * d_src, + khm_size cbd_src, + void * d_dst, + khm_size * cbd_dst); + +khm_int32 KHMAPI kcdb_type_int64_toString( + const void * d, + khm_size cbd, + wchar_t * buffer, + khm_size * cb_buf, + khm_int32 flags); + +khm_boolean KHMAPI kcdb_type_int64_isValid( + const void * d, + khm_size cbd); + +khm_int32 KHMAPI kcdb_type_int64_comp( + const void * d1, + khm_size cbd1, + const void * d2, + khm_size cbd2); + +khm_int32 KHMAPI kcdb_type_int64_dup( + const void * d_src, + khm_size cbd_src, + void * d_dst, + khm_size * cbd_dst); + +khm_int32 KHMAPI kcdb_type_data_toString( + const void * d, + khm_size cbd, + wchar_t * buffer, + khm_size * cb_buf, + khm_int32 flags); + +khm_boolean KHMAPI kcdb_type_data_isValid( + const void * d, + khm_size cbd); + +khm_int32 KHMAPI kcdb_type_data_comp( + const void * d1, + khm_size cbd1, + const void * d2, + khm_size cbd2); + +khm_int32 KHMAPI kcdb_type_data_dup( + const void * d_src, + khm_size cbd_src, + void * d_dst, + khm_size * cbd_dst); + +#endif \ No newline at end of file diff --git a/src/windows/identity/kherr/Makefile b/src/windows/identity/kherr/Makefile new file mode 100644 index 000000000..84f9da5ba --- /dev/null +++ b/src/windows/identity/kherr/Makefile @@ -0,0 +1,43 @@ +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +MODULE=kherr +!include <../config/Makefile.w32> + +INCFILES= \ + $(INCDIR)\kherr.h + +OBJFILES= \ + $(OBJ)\kherrmain.obj \ + $(OBJ)\kherr.obj + +LIBFILES= + +SDKLIBFILES= \ + strsafe.lib + +all: mkdirs $(INCFILES) $(OBJFILES) + +clean:: + $(RM) $(INCFILES) diff --git a/src/windows/identity/kherr/kherr.c b/src/windows/identity/kherr/kherr.c new file mode 100644 index 000000000..04b45238b --- /dev/null +++ b/src/windows/identity/kherr/kherr.c @@ -0,0 +1,1164 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include +#ifdef Debug +#include +#endif + +CRITICAL_SECTION cs_error; +DWORD tls_error = 0; +kherr_context * ctx_free_list = NULL; +kherr_context * ctx_root_list = NULL; +kherr_context * ctx_error_list = NULL; +kherr_event * evt_free_list = NULL; + +kherr_handler_node * ctx_handlers = NULL; +khm_size n_ctx_handlers; +khm_size nc_ctx_handlers; + +khm_ui_4 ctx_serial = 0; + +#ifdef DEBUG +static void DebugPrintf(wchar_t * fmt, ...) { + va_list vl; + wchar_t buf[1024]; + + va_start(vl, fmt); + StringCbVPrintf(buf, sizeof(buf), fmt, vl); + OutputDebugString(buf); + va_end(vl); +} +#endif + +KHMEXP void KHMAPI kherr_add_ctx_handler(kherr_ctx_handler h, + khm_int32 filter) { + + assert(h); + + EnterCriticalSection(&cs_error); + if( ctx_handlers == NULL) { + nc_ctx_handlers = CTX_ALLOC_INCR; + n_ctx_handlers = 0; + ctx_handlers = malloc(sizeof(*ctx_handlers) * nc_ctx_handlers); + /* No need to initialize */ + } else if (n_ctx_handlers == nc_ctx_handlers) { + khm_size new_nc; + kherr_handler_node * new_ctxs; + + new_nc = nc_ctx_handlers + CTX_ALLOC_INCR; + new_ctxs = malloc(sizeof(*new_ctxs) * new_nc); + memmove(new_ctxs, ctx_handlers, n_ctx_handlers); + + free(ctx_handlers); + ctx_handlers = new_ctxs; + nc_ctx_handlers = new_nc; + } + + if (filter == 0) + filter = KHERR_CTX_BEGIN | + KHERR_CTX_DESCRIBE | + KHERR_CTX_END | + KHERR_CTX_ERROR; + + ctx_handlers[n_ctx_handlers].h = h; + ctx_handlers[n_ctx_handlers].filter = filter; + LeaveCriticalSection(&cs_error); +} + +KHMEXP void KHMAPI kherr_remove_ctx_handler(kherr_ctx_handler h) { + khm_size i; + EnterCriticalSection(&cs_error); + + for (i=0 ; i < n_ctx_handlers; i++) { + if (ctx_handlers[i].h == h) { + break; + } + } + + if ( i < n_ctx_handlers ) { + n_ctx_handlers --; + for (; i < n_ctx_handlers; i++) { + ctx_handlers[i] = ctx_handlers[i + 1]; + } + } + + LeaveCriticalSection(&cs_error); +} + +/* Called with cs_error held */ +void notify_ctx_event(enum kherr_ctx_event e, kherr_context * c) { + khm_size i; + + for (i=0; inc_ctx = THREAD_STACK_SIZE; + t->n_ctx = 0; + t->ctx = (kherr_context **) &t[1]; + + TlsSetValue(tls_error, t); +} + +void detach_this_thread(void) { + kherr_thread * t; + khm_size i; + + t = (kherr_thread *) TlsGetValue(tls_error); + if (t) { + for(i=0; i < t->n_ctx; i++) { + kherr_release_context(t->ctx[i]); + } + free(t); + TlsSetValue(tls_error, 0); + } +} + +kherr_context * peek_context(void) { + kherr_thread * t; + + t = (kherr_thread *) TlsGetValue(tls_error); + if (t) { + if (t->n_ctx > 0) + return t->ctx[t->n_ctx - 1]; + else + return NULL; + } else + return NULL; +} + +void push_context(kherr_context * c) { + kherr_thread * t; + + t = (kherr_thread *) TlsGetValue(tls_error); + if (!t) { + attach_this_thread(); + t = (kherr_thread *) TlsGetValue(tls_error); + assert(t); + } + + if (t->n_ctx == t->nc_ctx) { + khm_size nc_new; + khm_size cb_new; + kherr_thread * nt; + + nc_new = t->nc_ctx + THREAD_STACK_SIZE; + cb_new = sizeof(kherr_thread) + + sizeof(kherr_context *) * nc_new; + + nt = malloc(cb_new); + memcpy(nt, t, sizeof(kherr_thread) + + sizeof(kherr_context *) * t->n_ctx); + nt->ctx = (kherr_context **) &nt[1]; + nt->nc_ctx = nc_new; + + free(t); + t = nt; + TlsSetValue(tls_error, t); + } + + assert(t->n_ctx < t->nc_ctx); + t->ctx[t->n_ctx++] = c; + + kherr_hold_context(c); +} + +/* returned pointer is still held */ +kherr_context * pop_context(void) { + kherr_thread * t; + kherr_context * c; + + t = (kherr_thread *) TlsGetValue(tls_error); + if (t) { + if (t->n_ctx > 0) { + c = t->ctx[--(t->n_ctx)]; + return c; + } else + return NULL; + } else { + return NULL; + } +} + +kherr_event * get_empty_event(void) { + kherr_event * e; + + EnterCriticalSection(&cs_error); + if(evt_free_list) + LPOP(&evt_free_list, &e); + else + e = malloc(sizeof(*e)); + LeaveCriticalSection(&cs_error); + ZeroMemory(e, sizeof(*e)); + e->severity = KHERR_NONE; + e->magic = KHERR_EVENT_MAGIC; + + return e; +} + +void free_event_params(kherr_event * e) { + if(parm_type(e->p1) == KEPT_STRINGT) { + assert((void *) parm_data(e->p1)); + free((void*) parm_data(e->p1)); + e->p1 = (kherr_param) 0; + } + if(parm_type(e->p2) == KEPT_STRINGT) { + assert((void *) parm_data(e->p2)); + free((void*) parm_data(e->p2)); + e->p2 = (kherr_param) 0; + } + if(parm_type(e->p3) == KEPT_STRINGT) { + assert((void *) parm_data(e->p3)); + free((void*) parm_data(e->p3)); + e->p3 = (kherr_param) 0; + } + if(parm_type(e->p4) == KEPT_STRINGT) { + assert((void *) parm_data(e->p4)); + free((void*) parm_data(e->p4)); + e->p4 = (kherr_param) 0; + } +} + +void free_event(kherr_event * e) { + assert(e->magic == KHERR_EVENT_MAGIC); + +#ifdef DEBUG + DebugPrintf(L"Freeing event 0x%x\n", e); + if (!(e->flags & KHERR_RF_STR_RESOLVED)) + resolve_event_strings(e); + if (e->short_desc) + DebugPrintf(L" Desc(S):[%s]\n", e->short_desc); + if (e->long_desc) + DebugPrintf(L" Desc(L):[%s]\n", e->long_desc); + if (e->suggestion) + DebugPrintf(L" Suggest:[%s]\n", e->suggestion); + if (e->facility) + DebugPrintf(L" Facility:[%s]\n", e->facility); +#endif + + if(e->flags & KHERR_RF_FREE_SHORT_DESC) { + assert(e->short_desc); + free((void *) e->short_desc); + } + if(e->flags & KHERR_RF_FREE_LONG_DESC) { + assert(e->long_desc); + free((void *) e->long_desc); + } + if(e->flags & KHERR_RF_FREE_SUGGEST) { + assert(e->suggestion); + free((void *) e->suggestion); + } + + free_event_params(e); + + ZeroMemory(e, sizeof(e)); + + EnterCriticalSection(&cs_error); + LPUSH(&evt_free_list, e); + LeaveCriticalSection(&cs_error); +} + +kherr_context * get_empty_context(void) { + kherr_context * c; + + EnterCriticalSection(&cs_error); + if(ctx_free_list) + LPOP(&ctx_free_list, &c); + else { + c = malloc(sizeof(kherr_context)); + } + + ZeroMemory(c,sizeof(*c)); + c->severity = KHERR_NONE; + c->flags = KHERR_CF_UNBOUND; + c->magic = KHERR_CONTEXT_MAGIC; + c->serial = ++ctx_serial; + + LPUSH(&ctx_root_list, c); + + LeaveCriticalSection(&cs_error); + + return c; +} + + +/* Assumes that the context has been deleted from all relevant + lists */ +void free_context(kherr_context * c) { + kherr_context * ch; + kherr_event * e; + + assert(c->magic == KHERR_CONTEXT_MAGIC); +#ifdef DEBUG + DebugPrintf(L"Freeing context 0x%x\n", c); +#endif + + EnterCriticalSection(&cs_error); + + if (c->desc_event) + free_event(c->desc_event); + c->desc_event = NULL; + + TPOPCHILD(c, &ch); + while(ch) { + free_context(ch); + TPOPCHILD(c, &ch); + } + QGET(c, &e); + while(e) { + free_event(e); + QGET(c, &e); + } + + c->serial = 0; + + LPUSH(&ctx_free_list,c); + LeaveCriticalSection(&cs_error); + +#ifdef DEBUG + DebugPrintf(L"Done with context 0x%x\n", c); +#endif +} + + +void add_event(kherr_context * c, kherr_event * e) +{ + EnterCriticalSection(&cs_error); + QPUT(c,e); + if(c->severity >= e->severity) { + if (e->severity <= KHERR_ERROR) + notify_ctx_event(KHERR_CTX_ERROR, c); + + c->severity = e->severity; + c->err_event = e; + c->flags &= ~KHERR_CF_DIRTY; + } + LeaveCriticalSection(&cs_error); +} + +void pick_err_event(kherr_context * c) +{ + kherr_event * e; + kherr_event * ce = NULL; + enum kherr_severity s; + + s = KHERR_RESERVED_BANK; + + EnterCriticalSection(&cs_error); + e = QTOP(c); + while(e) { + if(!(e->flags & KHERR_RF_INERT) && + s >= e->severity) { + ce = e; + s = e->severity; + } + e = QNEXT(e); + } + + if(ce) { + c->err_event = ce; + c->severity = ce->severity; + } else { + c->err_event = NULL; + c->severity = KHERR_NONE; + } + + c->flags &= ~KHERR_CF_DIRTY; + LeaveCriticalSection(&cs_error); +} + +static void arg_from_param(DWORD_PTR ** parm, kherr_param p) { + int t; + + if (p != 0) { + t = parm_type(p); + if (t == KEPT_INT32 || + t == KEPT_UINT32 || + t == KEPT_STRINGC || + t == KEPT_STRINGT) { + + *(*parm)++ = (DWORD_PTR) parm_data(p); + + } else if (t == KEPT_INT64 || + t == KEPT_UINT64) { + *(*parm)++ = (DWORD_PTR) parm_data(p) & 0xffffffff; + *(*parm)++ = (DWORD_PTR) (parm_data(p) >> 32) & 0xffffffff; + } else + *(*parm)++ = 0; + } +} + +/* The 'buf' parameter MUST point to a DWORD_PTR[8] array */ +static void args_from_event(DWORD_PTR * buf, kherr_event * e) { + arg_from_param(&buf, e->p1); + arg_from_param(&buf, e->p2); + arg_from_param(&buf, e->p3); + arg_from_param(&buf, e->p4); +} + +static void resolve_string_resource(kherr_event * e, + const wchar_t ** str, + khm_int32 if_flag, + khm_int32 or_flag) { + wchar_t tfmt[KHERR_MAXCCH_STRING]; + wchar_t tbuf[KHERR_MAXCCH_STRING]; + size_t chars; + size_t bytes; + + if(e->flags & if_flag) { + if(e->h_module != NULL) + chars = LoadString(e->h_module, (UINT)(INT_PTR) *str, + tfmt, ARRAYLENGTH(tbuf)); + if(e->h_module == NULL || chars == 0) + *str = NULL; + else { + wchar_t * s; + DWORD_PTR args[8]; + + args_from_event(args, e); + + chars = FormatMessage(FORMAT_MESSAGE_FROM_STRING | + FORMAT_MESSAGE_ARGUMENT_ARRAY, + tfmt, + 0, + 0, + tbuf, + ARRAYLENGTH(tbuf), + (va_list *) args); + + if (chars == 0) { + *str = NULL; + } else { + bytes = (chars + 1) * sizeof(wchar_t); + s = malloc(bytes); + assert(s); + StringCbCopy(s, bytes, tbuf); + *str = s; + e->flags |= or_flag; + } + } + e->flags &= ~if_flag; + } +} + +static void resolve_msg_resource(kherr_event * e, + const wchar_t ** str, + khm_int32 if_flag, + khm_int32 or_flag) { + wchar_t tbuf[KHERR_MAXCCH_STRING]; + size_t chars; + size_t bytes; + DWORD_PTR args[8]; + + if(e->flags & if_flag) { + if(e->h_module != NULL) { + args_from_event(args, e); + + chars = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | + FORMAT_MESSAGE_ARGUMENT_ARRAY, + (LPCVOID) e->h_module, + (DWORD)(DWORD_PTR) *str, + 0, + tbuf, + ARRAYLENGTH(tbuf), + (va_list *) args); + } + + if(e->h_module == NULL || chars == 0) { + *str = NULL; + } else { + wchar_t * s; + + /* MC inserts trailing \r\n to each message unless the + message is terminated with a %0. We remove the last + line break since it is irrelevant to our handling of + the string in the UI. */ + if (tbuf[chars-1] == L'\n') + tbuf[--chars] = L'\0'; + if (tbuf[chars-1] == L'\r') + tbuf[--chars] = L'\0'; + + bytes = (chars + 1) * sizeof(wchar_t); + s = malloc(bytes); + assert(s); + StringCbCopy(s, bytes, tbuf); + *str = s; + e->flags |= or_flag; + } + e->flags &= ~if_flag; + } +} + +static void resolve_string(kherr_event * e, + const wchar_t ** str, + khm_int32 mask, + khm_int32 free_if, + khm_int32 or_flag) { + + wchar_t tbuf[KHERR_MAXCCH_STRING]; + size_t chars; + size_t bytes; + DWORD_PTR args[8]; + + if (((e->flags & mask) == 0 || + (e->flags & mask) == free_if) && + *str != NULL) { + + args_from_event(args, e); + chars = FormatMessage(FORMAT_MESSAGE_FROM_STRING | + FORMAT_MESSAGE_ARGUMENT_ARRAY, + (LPCVOID) *str, + 0, + 0, + tbuf, + ARRAYLENGTH(tbuf), + (va_list *) args); + + if ((e->flags & mask) == free_if) { + free((void *) *str); + } + + e->flags &= ~mask; + + if (chars == 0) { + *str = 0; + } else { + wchar_t * s; + + bytes = (chars + 1) * sizeof(wchar_t); + s = malloc(bytes); + assert(s); + StringCbCopy(s, bytes, tbuf); + *str = s; + e->flags |= or_flag; + } + } + +} + +void resolve_event_strings(kherr_event * e) +{ + resolve_string(e, &e->short_desc, + KHERR_RFMASK_SHORT_DESC, + KHERR_RF_FREE_SHORT_DESC, + KHERR_RF_FREE_SHORT_DESC); + + resolve_string(e, &e->long_desc, + KHERR_RFMASK_LONG_DESC, + KHERR_RF_FREE_LONG_DESC, + KHERR_RF_FREE_LONG_DESC); + + resolve_string(e, &e->suggestion, + KHERR_RFMASK_SUGGEST, + KHERR_RF_FREE_SUGGEST, + KHERR_RF_FREE_SUGGEST); + + resolve_string_resource(e, &e->short_desc, + KHERR_RF_RES_SHORT_DESC, + KHERR_RF_FREE_SHORT_DESC); + + resolve_string_resource(e, &e->long_desc, + KHERR_RF_RES_LONG_DESC, + KHERR_RF_FREE_LONG_DESC); + + resolve_string_resource(e, &e->suggestion, + KHERR_RF_RES_SUGGEST, + KHERR_RF_FREE_SUGGEST); + + resolve_msg_resource(e, &e->short_desc, + KHERR_RF_MSG_SHORT_DESC, + KHERR_RF_FREE_SHORT_DESC); + resolve_msg_resource(e, &e->long_desc, + KHERR_RF_MSG_LONG_DESC, + KHERR_RF_FREE_LONG_DESC); + resolve_msg_resource(e, &e->suggestion, + KHERR_RF_MSG_SUGGEST, + KHERR_RF_FREE_SUGGEST); + + /* get rid of dangling reference now that we have done everything + we can with it. Since we have already dealt with all the + parameter inserts, we don't need the parameters anymore + either. */ + free_event_params(e); + + e->h_module = NULL; + e->flags |= KHERR_RF_STR_RESOLVED; +} + + +KHMEXP void KHMAPI kherr_evaluate_event(kherr_event * e) { + EnterCriticalSection(&cs_error); + resolve_event_strings(e); + LeaveCriticalSection(&cs_error); +} + +KHMEXP void KHMAPI kherr_evaluate_last_event(void) { + kherr_context * c; + kherr_event * e; + DWORD tid; + + c = peek_context(); + if(!c) + return; + tid = GetCurrentThreadId(); + + EnterCriticalSection(&cs_error); + e = QBOTTOM(c); + while (e != NULL && e->thread_id != tid) + e = QPREV(e); + + if(!e) + goto _exit; + + resolve_event_strings(e); + +_exit: + LeaveCriticalSection(&cs_error); +} + +KHMEXP kherr_event * KHMAPI +kherr_report(enum kherr_severity severity, + const wchar_t * short_desc, + const wchar_t * facility, + const wchar_t * location, + const wchar_t * long_desc, + const wchar_t * suggestion, + khm_int32 facility_id, + enum kherr_suggestion suggestion_id, + kherr_param p1, + kherr_param p2, + kherr_param p3, + kherr_param p4, + khm_int32 flags +#ifdef _WIN32 + ,HMODULE h_module +#endif + ) { + kherr_context * c; + kherr_event * e; + + /*TODO: sanity check flags (ISPOW2) */ + + e = get_empty_event(); + + e->thread_id = GetCurrentThreadId(); + e->time_ticks = GetTickCount(); + GetSystemTimeAsFileTime(&e->time_ft); + + e->severity = severity; + e->short_desc = short_desc; + e->facility = facility; + e->location = location; + e->long_desc = long_desc; + e->suggestion = suggestion; + e->facility_id = facility_id; + e->suggestion_id = suggestion_id; + e->p1 = p1; + e->p2 = p2; + e->p3 = p3; + e->p4 = p4; + e->flags = flags; +#ifdef _WIN32 + e->h_module = h_module; +#endif + + EnterCriticalSection(&cs_error); + c = peek_context(); + + if(!c) { + /* the reason why we are doing it this way is because p1..p4, + the descriptions and the suggestion may contain allocations + that has to be freed. In terms of performance we are + assuming that this case doesn't happen that much. Har + har */ + free_event(e); + e = NULL; + } else { + add_event(c,e); + } + + LeaveCriticalSection(&cs_error); + + return e; +} + +KHMEXP void KHMAPI kherr_suggest(wchar_t * suggestion, + enum kherr_suggestion suggestion_id, + khm_int32 flags) { + kherr_context * c; + kherr_event * e; + DWORD tid; + + if (flags != KHERR_RF_CSTR_SUGGEST && + flags != KHERR_RF_RES_SUGGEST && + flags != KHERR_RF_MSG_SUGGEST && + flags != KHERR_RF_FREE_SUGGEST) + return; + + c = peek_context(); + if(!c) + return; + + tid = GetCurrentThreadId(); + + EnterCriticalSection(&cs_error); + e = QBOTTOM(c); + while (e != NULL && e->thread_id != tid) + e = QPREV(e); + + if(!e) + goto _exit; + + /* if strings have already been resolved in this event, we cant + add any more unresolved strings. */ + if ((flags == KHERR_RF_RES_SUGGEST || + flags == KHERR_RF_MSG_SUGGEST) && + (e->flags & KHERR_RF_STR_RESOLVED)) + goto _exit; + + e->suggestion = suggestion; + e->suggestion_id = suggestion_id; + e->flags |= flags; +_exit: + LeaveCriticalSection(&cs_error); +} + +KHMEXP void KHMAPI kherr_location(wchar_t * location) { + kherr_context * c; + kherr_event * e; + DWORD tid; + + c = peek_context(); + if(!c) + return; + tid = GetCurrentThreadId(); + + EnterCriticalSection(&cs_error); + e = QBOTTOM(c); + while (e != NULL && e->thread_id != tid) + e = QPREV(e); + + if(!e) + goto _exit; + e->location = location; +_exit: + LeaveCriticalSection(&cs_error); +} + +KHMEXP void KHMAPI kherr_facility(wchar_t * facility, + khm_int32 facility_id) { + kherr_context * c; + kherr_event * e; + DWORD tid; + + c = peek_context(); + if(!c) + return; + tid = GetCurrentThreadId(); + EnterCriticalSection(&cs_error); + e = QBOTTOM(c); + while (e != NULL && e->thread_id != tid) + e = QPREV(e); + + if(!e) + goto _exit; + e->facility = facility; + e->facility_id = facility_id; +_exit: + LeaveCriticalSection(&cs_error); +} + +KHMEXP void KHMAPI kherr_set_desc_event(void) { + kherr_context * c; + kherr_event * e; + DWORD tid; + + c = peek_context(); + if(!c) + return; + tid = GetCurrentThreadId(); + + EnterCriticalSection(&cs_error); + e = QBOTTOM(c); + while (e != NULL && e->thread_id != tid) + e = QPREV(e); + + if(!e || c->desc_event) + goto _exit; + + QDEL(c,e); + c->desc_event = e; + e->severity = KHERR_NONE; + resolve_event_strings(e); + + notify_ctx_event(KHERR_CTX_DESCRIBE, c); + +_exit: + LeaveCriticalSection(&cs_error); +} + +KHMEXP void KHMAPI kherr_del_last_event(void) { + kherr_context * c; + kherr_event * e; + DWORD tid; + + c = peek_context(); + + if(!c) + return; + + tid = GetCurrentThreadId(); + + EnterCriticalSection(&cs_error); + e = QBOTTOM(c); + while (e != NULL && e->thread_id != tid) + e = QPREV(e); + + if(e) { + QDEL(c, e); + if(c->err_event == e) { + pick_err_event(c); + } + free_event(e); + } + LeaveCriticalSection(&cs_error); +} + +KHMEXP void KHMAPI kherr_push_context(kherr_context * c) +{ + kherr_context * p; + int new_context = FALSE; + + EnterCriticalSection(&cs_error); + p = peek_context(); + if(p && (c->flags & KHERR_CF_UNBOUND)) { + LDELETE(&ctx_root_list, c); + TADDCHILD(p,c); + c->flags &= ~KHERR_CF_UNBOUND; + kherr_hold_context(p); + new_context = TRUE; + } + push_context(c); + + if (new_context) + notify_ctx_event(KHERR_CTX_BEGIN, c); + + LeaveCriticalSection(&cs_error); +} + +KHMEXP void KHMAPI kherr_push_new_context(khm_int32 flags) +{ + kherr_context * p; + kherr_context * c; + + flags &= KHERR_CFMASK_INITIAL; + + EnterCriticalSection(&cs_error); + p = peek_context(); + c = get_empty_context(); + if(p) { + LDELETE(&ctx_root_list, c); + TADDCHILD(p,c); + c->flags &= ~KHERR_CF_UNBOUND; + kherr_hold_context(p); + } + c->flags |= flags; + push_context(c); + + notify_ctx_event(KHERR_CTX_BEGIN, c); + + LeaveCriticalSection(&cs_error); +} + +kherr_param dup_parm(kherr_param p) { + if(parm_type(p) == KEPT_STRINGT) { + wchar_t * d = wcsdup((wchar_t *)parm_data(p)); + return kherr_val(KEPT_STRINGT, d); + } else + return p; +} + +kherr_event * fold_context(kherr_context * c) { + kherr_event * e; + kherr_event * g; + + if (!c) + return NULL; + + EnterCriticalSection(&cs_error); + if(!c->err_event || (c->flags & KHERR_CF_DIRTY)) { + pick_err_event(c); + } + if(c->err_event) { + g = c->err_event; + e = get_empty_event(); + *e = *g; + g->short_desc = NULL; + g->long_desc = NULL; + g->suggestion = NULL; + g->flags &= + ~(KHERR_RF_FREE_SHORT_DESC | + KHERR_RF_FREE_LONG_DESC | + KHERR_RF_FREE_SUGGEST); + LINIT(e); + e->p1 = dup_parm(g->p1); + e->p2 = dup_parm(g->p2); + e->p3 = dup_parm(g->p3); + e->p4 = dup_parm(g->p4); + } else { + e = c->desc_event; + c->desc_event = NULL; + } + + if (e) + e->flags |= KHERR_RF_CONTEXT_FOLD; + + LeaveCriticalSection(&cs_error); + + return e; +} + +KHMEXP void KHMAPI kherr_hold_context(kherr_context * c) { + assert(c && c->magic == KHERR_CONTEXT_MAGIC); + EnterCriticalSection(&cs_error); + c->refcount++; + LeaveCriticalSection(&cs_error); +} + +KHMEXP void KHMAPI kherr_release_context(kherr_context * c) { + assert(c && c->magic == KHERR_CONTEXT_MAGIC); + EnterCriticalSection(&cs_error); + c->refcount--; + if (c->refcount == 0) { + kherr_event * e; + kherr_context * p; + + notify_ctx_event(KHERR_CTX_END, c); + + p = TPARENT(c); + if (p) { + e = fold_context(c); + if (e) + add_event(p, e); + + TDELCHILD(p, c); + kherr_release_context(p); + } else { + LDELETE(&ctx_root_list, c); + } + free_context(c); + } + LeaveCriticalSection(&cs_error); +} + +KHMEXP void KHMAPI kherr_pop_context(void) { + kherr_context * c; + + EnterCriticalSection(&cs_error); + c = pop_context(); + if(c) { + kherr_release_context(c); + } + LeaveCriticalSection(&cs_error); +} + +KHMEXP kherr_context * KHMAPI kherr_peek_context(void) { + kherr_context * c; + + c = peek_context(); + if (c) + kherr_hold_context(c); + + return c; +} + +KHMEXP khm_boolean KHMAPI kherr_is_error(void) { + kherr_context * c = peek_context(); + return kherr_is_error_i(c); +} + +KHMEXP khm_boolean KHMAPI kherr_is_error_i(kherr_context * c) { + if(c && c->severity <= KHERR_ERROR) + return TRUE; + else + return FALSE; +} + +KHMEXP void KHMAPI kherr_clear_error(void) { + kherr_context * c = peek_context(); + if (c) + kherr_clear_error_i(c); +} + +KHMEXP void KHMAPI kherr_clear_error_i(kherr_context * c) { + kherr_event * e; + if (c) { + EnterCriticalSection(&cs_error); + e = QTOP(c); + while(e) { + e->flags |= KHERR_RF_INERT; + e = QNEXT(e); + } + c->severity = KHERR_NONE; + c->err_event = NULL; + c->flags &= ~KHERR_CF_DIRTY; + LeaveCriticalSection(&cs_error); + } +} + +KHMEXP void KHMAPI kherr_set_progress(khm_ui_4 num, khm_ui_4 denom) { + kherr_context * c = peek_context(); + if(c) { + EnterCriticalSection(&cs_error); + c->progress_denom = denom; + c->progress_num = num; + LeaveCriticalSection(&cs_error); + } +} + +KHMEXP void KHMAPI kherr_get_progress(khm_ui_4 * num, khm_ui_4 * denom) { + kherr_context * c = peek_context(); + kherr_get_progress_i(c,num,denom); +} + +KHMEXP void KHMAPI kherr_get_progress_i(kherr_context * c, + khm_ui_4 * num, + khm_ui_4 * denom) { + if(c) { + EnterCriticalSection(&cs_error); + *num = c->progress_num; + *denom = c->progress_denom; + LeaveCriticalSection(&cs_error); + } else { + *num = 0; + *denom = 0; + } +} + +KHMEXP kherr_event * KHMAPI kherr_get_first_event(kherr_context * c) +{ + kherr_event * e; + EnterCriticalSection(&cs_error); + e = QTOP(c); + LeaveCriticalSection(&cs_error); + return e; +} + +KHMEXP kherr_event * KHMAPI kherr_get_next_event(kherr_event * e) +{ + kherr_event * ee; + + EnterCriticalSection(&cs_error); + ee = QNEXT(e); + LeaveCriticalSection(&cs_error); + return ee; +} + +KHMEXP kherr_context * KHMAPI kherr_get_first_context(kherr_context * c) +{ + kherr_context * cc; + + EnterCriticalSection(&cs_error); + if (c) { + cc = TFIRSTCHILD(c); + if (cc) + kherr_hold_context(cc); + } else { + cc = ctx_root_list; + if (cc) + kherr_hold_context(cc); + } + LeaveCriticalSection(&cs_error); + return cc; +} + +KHMEXP kherr_context * KHMAPI kherr_get_next_context(kherr_context * c) +{ + kherr_context * cc; + EnterCriticalSection(&cs_error); + cc = LNEXT(c); + if (cc) + kherr_hold_context(cc); + LeaveCriticalSection(&cs_error); + return cc; +} + +KHMEXP kherr_event * KHMAPI kherr_get_err_event(kherr_context * c) +{ + kherr_event * e; + EnterCriticalSection(&cs_error); + if(!c->err_event) { + pick_err_event(c); + } + e = c->err_event; + LeaveCriticalSection(&cs_error); + return e; +} + +KHMEXP kherr_event * KHMAPI kherr_get_desc_event(kherr_context * c) +{ + kherr_event * e; + + EnterCriticalSection(&cs_error); + e = c->desc_event; + LeaveCriticalSection(&cs_error); + return e; +} + +KHMEXP kherr_param kherr_dup_string(const wchar_t * s) +{ + wchar_t * dest; + size_t cb_s; + + if (s == NULL) + return (kherr_param) 0; + + if (FAILED(StringCbLength(s, KHERR_MAXCB_STRING, &cb_s))) + cb_s = KHERR_MAXCB_STRING; + else + cb_s += sizeof(wchar_t); + + dest = malloc(cb_s); + assert(dest != NULL); + dest[0] = L'\0'; + + StringCbCopy(dest, cb_s, s); + + return _tstr(dest); +} diff --git a/src/windows/identity/kherr/kherr.h b/src/windows/identity/kherr/kherr.h new file mode 100644 index 000000000..7ccc6e560 --- /dev/null +++ b/src/windows/identity/kherr/kherr.h @@ -0,0 +1,965 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KHERR_H +#define __KHIMAIRA_KHERR_H + +/*! \defgroup kherr NetIDMgr Error Reporting + + Error reporting functions provide a mechanism to construct + meaningful and user friendly error reports for the user. + + Unlike most of the other NetIDMgr API's, the error reporting APIs + are lightweight and usually do not return an error value. This is + mostly because, these functions are called \b after an error + occurs. + + @{*/ +#include +#include + +/*! \name Customizable macros +@{ */ +#ifndef KHERR_FACILITY +/*! \brief The default facility when reporting errors + + When including this header file, if the KHERR_FACILITY macro is + defined to be a wide character string, then it will be used as the + default facility when for the convenience macros. All of the + calls to the convenience macros in the source file would then have + that facility. + + If left undefined, the convenience macros will leave the facility + value undefined. + */ +#define KHERR_FACILITY NULL +#endif + +#ifndef KHERR_FACILITY_ID +/*! \brief The default facility ID when reporting errors + + When including this header file, if the KHERR_FACILITY_ID macro is + defined to be non-zero, then it will be used as the default + facility identifier for the convenience macros. All of the calls + to the convenience macros in the source file would then have that + facility identifier. + + The default value of 0 means that the facility is undefined. + */ +#define KHERR_FACILITY_ID 0 +#endif + +/*! \define KHERR_HMODULE (undefined) + \brief The default module handle + + When including this header file, if the KHERR_HMODULE macro is + defined to be an identifier that holds the module handle, then the + convenience macros that specify a module handle will use it. + + A default value is not defined for KHERR_HMODULE. Any attempt to + invoke any of the convenience macros that use it should generate a + compile time error. + */ +#ifdef _WIN32 +#ifndef KHERR_HMODULE +#endif +#endif +/*@}*/ + +/*! \brief Parameter types + */ +enum kherr_parm_types { + KEPT_INT32 = 1, + KEPT_UINT32, + KEPT_INT64, + KEPT_UINT64, + KEPT_STRINGC, /*!< String constant */ + KEPT_STRINGT /*!< String. Will be freed using + free() when the event is freed */ +}; + +#ifdef _WIN32 +typedef khm_ui_8 kherr_param; +#else +#error kherr_param undefined +#endif + +/*! \brief Severity levels + + Larger the value, the less severe it is. +*/ +enum tag_kherr_severity { + KHERR_FATAL = 0, /*!< Fatal error.*/ + KHERR_ERROR, /*!< Non-fatal error. We'll probably + survive. See the suggested action. */ + KHERR_WARNING, /*!< Warning. Something almost broke + or soon will. See the suggested + action. */ + KHERR_INFO, /*!< Informational. Something happened + that we would like you to know + about. */ + KHERR_DEBUG_3 = 64, /*!< Verbose debug level 3 (high) */ + KHERR_DEBUG_2 = 65, /*!< Verbose debug level 2 (medium) */ + KHERR_DEBUG_1 = 66, /*!< Verbose debug level 1 (low) */ + KHERR_RESERVED_BANK = 127, /*!< Internal use */ + KHERR_NONE = 128 /*!< Nothing interesting has happened + so far */ +}; + +typedef enum tag_kherr_severity kherr_severity; + +/*! \brief Suggestions */ +enum tag_kherr_suggestion { + KHERR_SUGGEST_NONE = 0, /*!< No suggestions. */ + KHERR_SUGGEST_ABORT, /*!< Abort whatever it was you were + trying. It's not gonna work. */ + KHERR_SUGGEST_RETRY, /*!< Retry. It might work the second + or third time over */ + KHERR_SUGGEST_IGNORE, /*!< Ignore. It might go away. */ + KHERR_SUGGEST_INTERACT, /*!< Further user interaction is + necessary to resolve the situation. + The suggest string in the event + should be prompted to the user. */ + KHERR_SUGGEST_OTHER, /*!< Something else. */ +}; + +typedef enum tag_kherr_suggestion kherr_suggestion; + +/*! \brief An event */ +typedef struct tag_kherr_event { + khm_int32 magic; /*!< Magic number. Always set to + KHERR_EVENT_MAGIC */ + DWORD thread_id; /*!< The thread which reported this + event. */ + const wchar_t * short_desc; /*!< Short description or title + (localized) */ + const wchar_t * facility; /*!< Facility name of the reporter + (not localized) */ + const wchar_t * location; /*!< Location. Usually the function + name or such of where the event + occured (not localized) */ + const wchar_t * long_desc; /*!< A long description of what went + wrong (localized, formatted) */ + const wchar_t * suggestion; /*!< A suggested way to fix it + (localized,formatted) */ + + kherr_severity severity; + /*!< Severity level. One of the + severity levels listed in + enumeration ::kherr_severity */ + khm_int32 facility_id; /*!< Left to the application to + interpret */ + kherr_suggestion suggestion_id; + /*!< One of the suggestion ID's from + the enumeration + ::kherr_suggestion */ + + int flags; /*!< Flags. */ + + kherr_param p1; /*!< Parameter 1 for formatting */ + kherr_param p2; /*!< Parameter 2 for formatting */ + kherr_param p3; /*!< Parameter 3 for formatting */ + kherr_param p4; /*!< Parameter 4 for formatting */ + + DWORD time_ticks; /*!< Time at which event was reported + (as returned by GetTickCount(). */ + FILETIME time_ft; /*!< Time at which event was reported. + Current system time as FILETIME. */ + +#ifdef _WIN32 + HMODULE h_module; /*!< Handle to the module which should + resolve any unresolved resources + references above. */ +#endif + + LDCL(struct tag_kherr_event); +} kherr_event; + +#define KHERR_EVENT_MAGIC 0x0423e84f + +/*! \brief Flags for kherr_event + + Each set of flags that define the type of resource for one value + is mutually exclusive. + */ +enum kherr_event_flags { + KHERR_RF_CSTR_SHORT_DESC= 0x00000000, + /*!< Short description is a constant + string */ + KHERR_RF_RES_SHORT_DESC = 0x00000001, + /*!< Short description is a string + resource */ + KHERR_RF_MSG_SHORT_DESC = 0x00000002, + /*!< Short description is a message + resource */ + KHERR_RF_FREE_SHORT_DESC= 0x00000004, + /*!< Short description is an allocated + string */ + KHERR_RFMASK_SHORT_DESC = 0x00000007, + + KHERR_RF_CSTR_LONG_DESC = 0x00000000, + /*!< Long description is a constant + string */ + KHERR_RF_RES_LONG_DESC = 0x00000008, + /*!< Long description is a string + resource */ + KHERR_RF_MSG_LONG_DESC = 0x00000010, + /*!< Long description is a message + resouce */ + KHERR_RF_FREE_LONG_DESC = 0x00000020, + /*!< Long description is an allocated + string */ + KHERR_RFMASK_LONG_DESC = 0x00000038, + + KHERR_RF_CSTR_SUGGEST = 0x00000000, + /*!< Suggestion is a constant + string */ + KHERR_RF_RES_SUGGEST = 0x00000040, + /*!< Suggestion is a string + resource */ + KHERR_RF_MSG_SUGGEST = 0x00000080, + /*!< Suggestion is a message + resource */ + KHERR_RF_FREE_SUGGEST = 0x00000100, + /*!< Suggestion is an allocated + string */ + KHERR_RFMASK_SUGGEST = 0x000001C0, + + KHERR_RF_STR_RESOLVED = 0x00010000, + /*!< The string resources in the event + have been resolved. */ + KHERR_RF_CONTEXT_FOLD = 0x00020000, + /*!< The event is a representation of + a folded context. */ + + KHERR_RF_INERT = 0x00040000 + /*!< Inert event. The event has + already been dealt with and is no + longer considered significant. */ +}; + +/*! \brief An error context +*/ +typedef struct tag_kherr_context { + khm_int32 magic; /*!< Magic number. Always set to + KHERR_CONTEXT_MAGIC */ + + khm_ui_4 serial; /*!< Context instance serial number. + Context objects themselves may be + reused for different contexts as + they are freed and reallocated. + However every instance of a context + is guaranteed to have a unique + serial number as specified in this + field. If an external entity wants + to keep track of the context, it + should keep track of the serial + number as well as the pointer to the + context object. */ + + kherr_severity severity; + /*!< Severity level. One of the + severity levels listed below. This + is the severity level of the context + and is the maximum severity level of + all the events in the queue of + events. */ + + khm_int32 flags; /*!< Flags. Used internally. */ + khm_ui_4 refcount; /*!< Reference count. Used + internally */ + + kherr_event *desc_event; /*!< Description event. The event that + describes the error context. This + points to an event that is not in + the event queue. */ + + kherr_event *err_event; /*!< Significant event. The last one + that caused the severity level to be + what it is right now. This points + to an event that is listed in the + event queue for this context.*/ + + khm_ui_4 progress_num; /*!< Progress numerator */ + khm_ui_4 progress_denom; /*!< Progress denominator */ + + TDCL(struct tag_kherr_context); + QDCL(struct tag_kherr_event); +} kherr_context; + +#define KHERR_CONTEXT_MAGIC 0x34f3238c + +enum kherr_context_flags { + KHERR_CF_NONE = 0x00000000, + /*!< None. */ + + KHERR_CF_DIRTY = 0x00000001, + /*!< Used Internally. Denotes that + the err_event and severity may need + to be recalculated. Cannot be set + as an initial flag. */ + + KHERR_CF_OWN_PROGRESS = 0x00000002, + /*!< The context maintains its own + progress meter as opposed to one + that is derived from child + contexts. */ + + KHERR_CF_UNBOUND = 0x00000004, + /*!< Unbound context. The context + can't be used to log events. Call + kherr_push_context() to associate + the context with the global context + hierarchy. Cannot be set as an + initial flag. */ + + KHERR_CF_TRANSITIVE = 0x00000008, + /*!< Transitive. The context is + automatically made the current + context for all other threads that + handle messages sent or posted by + threads whose current error context + is this one. */ + + KHERR_CFMASK_INITIAL = 0x0000000a, + /*!< Allowed initial flags */ +}; + +/*! \brief Maximum length of a string field in characters including terminating NULL + */ +#define KHERR_MAXCCH_STRING 1024 + +/*! \brief Maximum length of a string field in bytes including terminating NULL + */ +#define KHERR_MAXCB_STRING (KHERR_MAXCCH_STRING * sizeof(wchar_t)) + +/*! \brief Context event + + \see kherr_add_ctx_handler() +*/ +enum kherr_ctx_event { + KHERR_CTX_BEGIN = 0x0001, /*!< A new context was created */ + KHERR_CTX_DESCRIBE=0x0002, /*!< A context was described */ + KHERR_CTX_END = 0x0004, /*!< A context was closed */ + KHERR_CTX_ERROR = 0x0008 /*!< A context switched to an error + state */ +}; + +/*! \brief Context event handler + + Context event handlers are invoked when specific events occur with + respect to an error context. The ::kherr_ctx_event parameter + specifies which event occurred using one of the event values + described in the enumeration. The error context in which this + event occurred is specified by the ::kherr_context pointer. + + Note that if the handler needs to keep track of the error context + for later processing, it also needs to keep track of the \a serial + field of the error context. The same context object may be + reused, but the serial number is guaranteed to be unique. + + \see kherr_add_ctx_handler() + */ +typedef void (*kherr_ctx_handler)(enum kherr_ctx_event, kherr_context *); + +/*! \brief Add a context event handler + + An application can register an event handler that gets notified of + events that pertain to error contexts. More than one handler can + be registered. The order in which the handlers are called is + undefined for any specific event. + + These event occur in the context of individual application + threads. The handler will be called from within the thread that + caused the event. Therefore it is important that the handler is + both reentrant and returns quickly. + + The events that the handler will be notified of are explained + below: + + KHERR_CTX_BEGIN: Notification that a new context was + created. A pointer to the context will be supplied to the + handler. The supplied pointer should not be used to obtain a hold + on the context, as it will prevent the context from being closed. + + KHERR_CTX_DESCRIBE: The thread called + kherr_set_desc_event() to set the description of a context. Once + again, the pointer should not be used to obtain a hold on the + context. + + KHERR_CTX_ERROR: The last event that was reported for the + context was an error event (the severity was was equal or higher + than KHERR_ERROR). The pointer may be used to obtain a hold on + the context. However, it is the application's resonsibility to + make sure that the hold is released later. Otherwise the event + will never be closed. + + KHERR_CTX_END: Closure. This event is signalled when the + last open handle to the context is closed and there is no thread + that is currently active which has this context in its error + context stack. At the time the handler is invoked, the context is + still intact. The pointer that is supplied should not be used to + obtain a handle on the context. + + \param[in] h Context event handler, of type ::kherr_ctx_handler + + \param[in] filter A combination of ::kherr_ctx_event values + indication which notifications should be sent to the handler. + If a \a filter value of zero is provided, all of the events + will be sent to the handler. + */ +KHMEXP void KHMAPI kherr_add_ctx_handler(kherr_ctx_handler h, + khm_int32 filter); + +/*! \brief Remove a context event handler + + Undoes what was done with kherr_add_ctx_handler() + + \see kherr_add_ctx_handler() + */ +KHMEXP void KHMAPI kherr_remove_ctx_handler(kherr_ctx_handler h); + + +/*! \brief Report an error + + Creates an event, fills in the details specified in the arguments, + and adds it to the current error context. + + If the current thread does not have an error context, no reporting + happens. However, if any of the supplied strings or parameters + are marked as allocated, they will be freed before the function + returns. + + Certain parameters that expect strings can instead be given string + resources, message resources or allocated strings in addition to + constant string. By default, the parameters are expected to be + constant strings. + + Allocated strings: The application can allocate memory for + a string. Since the application is not notified when the event is + no longer used and freed, it \b must indicate that the string is + an allocated string by setting the appropriate flag in the \a + flags parameter. When the event is no longer used, the memory + pointed to by the relevant pointer will be freed through a call to + free(). Not all string parameters take allocated strings. See + individual parameter documentation for details. + + String resources: On WIN32, string resources can be passed + in to kherr_report() using the MAKEINTRESOURCE macro. However, + the application \b must specify that the parameter is a string + resource using the appropriate flag in the \a flags parameter. + The error reporting engine will expand the string against the + module handle passed in the \a h_module parameter when the value + of the string is required. Not all string parameters take string + resources. See individual parameter documentation for details. + Strings loaded through string resources cannot be longer than + ::KHERR_MAXCCH_STRING in characters inclusive of terminating NULL. + + Message resources: On WIN32, message resources can be + passed in to kherr_report() by specifying the message ID where it + ordinarily expects a pointer to a constant string. The + application \b must indicate that the string is a message resource + by using the appropriate flag in the \a flags parameter. When the + value of the string is needed, it is expanded against the module + handle passed in the \a h_module parameter using the message ID. + Not all string parameters take message resources. See individual + parameter documentation for details. Note that the facility and + severity values associated with a message resource are ignored. + Strings loaded through message resources cannot be longer than + ::KHERR_MAXCCH_STRING in characters inclusive of terminating NULL. + + Formatted fields: Parameters that are formatted can have + can have parameter inserts like in printf(). However, specifying + inserts is different from printf() and follows the conventions + used in WIN32 API FormatMessage(). This is because for localized + strings, the order of the parameters in the string may be + different. See the documentation for FormatMessage() for details + on the format string. The same set of parameters (i.e. \a p1, \a + p2, \a p3, \a p4) is used for all formatted strings with + appropriate marshalling for 64 bit types. The size of the string + after expansion must not exceed 65536 bytes inclusive of + terminating NULL. + + \param[in] severity One of ::kherr_severity_level + \param[in] short_desc Short description or title (localized). Can + be a string resource, message resource, allocated string or + constant string. The \a flags parameter should indicate the + type of string used. + \param[in] facility Facility name of the reporter (not localized) + \param[in] location Usually the function name or such of where the + event occured (not localized) + \param[in] long_desc Long description of event (localized, + formatted). Can be a string resource, message resource, + allocated string or constant string. The \a flags parameter + should indicate the type of string used. + \param[in] suggestion Suggested action to correct situation, if + applicable (localized). Can be a string resource, message + resource, allocated string or constant string. The \a flags + parameter should indicate the type of string used. + \param[in] facility_id Identifier of facility. Application + defined. + \param[in] suggestion_id One of the suggestion identifiers from + ::kherr_suggestion_ids + \param[in] p1 First parameter. Used for formatting. + \param[in] p2 Second parameter. Used for formatting. + \param[in] p3 Third parameter. Used for formatting. + \param[in] p4 Fourth parameter. Used for formatting. + \param[in] flags Flags. See ::kherr_report_flags + \param[in] h_module Handle to a module that resolves any string or + message resources used for the \a short_description , \a + long_desc or \a suggestion parameters. This parameter is only + available on WIN32. + + \note With the exception of parameters of type KEPT_STRINGT and + parameters which are flagged for freeing using the \a flags + parameter, all other string parameters are assumed to be + pointers to constant strings. The strings are not copied and + the pointers are used as is. Also, no clean-up is performed + when the event is freed other than that implied by \a flags. + */ +KHMEXP kherr_event * KHMAPI kherr_report( + enum kherr_severity severity, + const wchar_t * short_desc, + const wchar_t * facility, + const wchar_t * location, + const wchar_t * long_desC, + const wchar_t * suggestion, + khm_int32 facility_id, + enum kherr_suggestion suggestion_id, + kherr_param p1, + kherr_param p2, + kherr_param p3, + kherr_param p4, + khm_int32 flags +#ifdef _WIN32 + ,HMODULE h_module +#endif +); + +/*! \brief Create a parameter out of a transient string + + A parameter is created by duplicating the string that is passed + into the function. If the string exceeds KHERR_MAXCCH_STRING, + then only the first part of the string that fits within the limit + is duplicated. + + The resulign ::kherr_param must be passed in to kherr_report(). + The event logging framework will free the duplicated string once + the data is no longer required. + */ +KHMEXP kherr_param kherr_dup_string(const wchar_t * s); + +/* convenience macros for specifying parameters for kherr_report */ +#define kherr_val(type,val) \ + ((((kherr_param)(type)) << ((sizeof(kherr_param)-1)*8)) | (kherr_param) (val)) + +#define _int32(i) kherr_val(KEPT_INT32, i) +#define _uint32(ui) kherr_val(KEPT_UINT32, ui) +#define _int64(i) kherr_val(KEPT_INT64, i) +#define _uint64(ui) kherr_val(KEPT_UINT64, ui) +#define _cstr(cs) kherr_val(KEPT_STRINGC, cs) +#define _tstr(ts) kherr_val(KEPT_STRINGT, ts) +#define _dupstr(s) kherr_dup_string(s) + +/* convenience macros for calling kherr_report */ +#ifdef KHERR_HMODULE + +#define _report_cs0(severity, long_description) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, 0, 0, 0, 0, 0, KHERR_HMODULE) + +#define _report_cs1(severity, long_description, p1) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, p1, 0, 0, 0, 0, KHERR_HMODULE) + +#define _report_cs2(severity, long_description, p1, p2) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, p1, p2, 0, 0, 0, KHERR_HMODULE) + +#define _report_cs3(severity, long_description, p1, p2, p3) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, p1, p2, p3, 0, 0, KHERR_HMODULE) + +#define _report_cs4(severity, long_description, p1, p2, p3, p4) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, p1, p2, p3, p4, 0, KHERR_HMODULE) + +#else + +#define _report_cs0(severity, long_description) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, 0, 0, 0, 0, 0, NULL) + +#define _report_cs1(severity, long_description, p1) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, p1, 0, 0, 0, 0, NULL) + +#define _report_cs2(severity, long_description, p1, p2) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, p1, p2, 0, 0, 0, NULL) + +#define _report_cs3(severity, long_description, p1, p2, p3) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, p1, p2, p3, 0, 0, NULL) + +#define _report_cs4(severity, long_description, p1, p2, p3, p4) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_description), NULL, KHERR_FACILITY_ID, 0, p1, p2, p3, p4, 0, NULL) +#endif /* !defined(KHERR_HMODULE) */ + +#ifdef _WIN32 +#define _report_sr0(severity, long_desc_id) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, MAKEINTRESOURCE(long_desc_id), NULL, KHERR_FACILITY_ID, 0, 0, 0, 0, 0, KHERR_RF_RES_LONG_DESC, KHERR_HMODULE) + +#define _report_sr1(severity, long_desc_id, p1) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, MAKEINTRESOURCE(long_desc_id), NULL, KHERR_FACILITY_ID, 0, p1, 0, 0, 0, KHERR_RF_RES_LONG_DESC, KHERR_HMODULE) + +#define _report_sr2(severity, long_desc_id, p1, p2) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, MAKEINTRESOURCE(long_desc_id), NULL, KHERR_FACILITY_ID, 0, p1, p2, 0, 0, KHERR_RF_RES_LONG_DESC, KHERR_HMODULE) + +#define _report_sr3(severity, long_desc_id, p1, p2, p3) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, MAKEINTRESOURCE(long_desc_id), NULL, KHERR_FACILITY_ID, 0, p1, p2, p3, 0, KHERR_RF_RES_LONG_DESC, KHERR_HMODULE) + +#define _report_sr4(severity, long_desc_id, p1, p2, p3, p4) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, MAKEINTRESOURCE(long_desc_id), NULL, KHERR_FACILITY_ID, 0, p1, p2, p3, p4, KHERR_RF_RES_LONG_DESC, KHERR_HMODULE) +#endif + +#ifdef _WIN32 +#define _report_mr0(severity, long_desc_msg_id) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (wchar_t *)(long_desc_msg_id), NULL, KHERR_FACILITY_ID, 0, 0, 0, 0, 0, KHERR_RF_MSG_LONG_DESC, KHERR_HMODULE) + +#define _report_mr1(severity, long_desc_msg_id, p1) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (wchar_t *)(long_desc_msg_id), NULL, KHERR_FACILITY_ID, 0, p1, 0, 0, 0, KHERR_RF_MSG_LONG_DESC, KHERR_HMODULE) + +#define _report_mr2(severity, long_desc_msg_id, p1, p2) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (wchar_t *)(long_desc_msg_id), NULL, KHERR_FACILITY_ID, 0, p1, p2, 0, 0, KHERR_RF_MSG_LONG_DESC, KHERR_HMODULE) + +#define _report_mr3(severity, long_desc_msg_id, p1, p2, p3) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (wchar_t *)(long_desc_msg_id), NULL, KHERR_FACILITY_ID, 0, p1, p2, p3, 0, KHERR_RF_MSG_LONG_DESC, KHERR_HMODULE) + +#define _report_mr4(severity, long_desc_msg_id, p1, p2, p3, p4) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (wchar_t *)(long_desc_msg_id), NULL, KHERR_FACILITY_ID, 0, p1, p2, p3, p4, KHERR_RF_MSG_LONG_DESC, KHERR_HMODULE) +#endif + +#define _report_ts0(severity, long_desc_ptr) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_desc_ptr), NULL, KHERR_FACILITY_ID, 0, 0, 0, 0, 0, KHERR_RF_FREE_LONG_DESC, NULL) + +#define _report_ts1(severity, long_desc_ptr, p1) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_desc_ptr), NULL, KHERR_FACILITY_ID, 0, p1, 0, 0, 0, KHERR_RF_FREE_LONG_DESC, NULL) + +#define _report_ts2(severity, long_desc_ptr, p1, p2) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_desc_ptr), NULL, KHERR_FACILITY_ID, 0, p1, p2, 0, 0, KHERR_RF_FREE_LONG_DESC, NULL) + +#define _report_ts3(severity, long_desc_ptr, p1, p2, p3) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_desc_ptr), NULL, KHERR_FACILITY_ID, 0, p1, p2, p3, 0, KHERR_RF_FREE_LONG_DESC, NULL) + +#define _report_ts4(severity, long_desc_ptr, p1, p2, p3, p4) \ + kherr_report((severity), NULL, KHERR_FACILITY, NULL, (long_desc_ptr), NULL, KHERR_FACILITY_ID, 0, p1, p2, p3, p4, KHERR_RF_FREE_LONG_DESC, NULL) + +/*! \brief Set the suggestion and suggestion identifier for the last event + + The event that will be modified is the last event reported by the + calling thread. + */ +KHMEXP void KHMAPI kherr_suggest(wchar_t * suggestion, khm_int32 suggestion_id, khm_int32 flags); +#define _suggest_cs(cs,sid) kherr_suggest((cs), (sid), KHERR_RF_CSTR_SUGGEST) +#define _suggest_ts(ts,sid) kherr_suggest((ts), (sid), KHERR_RF_FREE_SUGGEST) +#define _suggest_sr(sr,sid) kherr_suggest(MAKEINTRESOURCE(sr), (sid), KHERR_RF_RES_SUGGEST) +#define _suggest_mr(mr,sid) kherr_suggest((wchar_t *)(DWORD_PTR)(mr), (sid), KHERR_RF_MSG_SUGGEST) + +/*! \brief Set the location string for the last event + + The event that will be modified is the last event reported by the + calling thread. + */ +KHMEXP void KHMAPI kherr_location(wchar_t * location); +#define _location(l) kherr_location(l) + +/*! \brief Set the facility string and identifier for the last event + + The event that will be modified is the last event reported by the + calling thread. + */ +KHMEXP void KHMAPI kherr_facility(wchar_t * facility, khm_int32 facility_id); +#define _facility(f,fid) kherr_facility((f),(fid)) + +/*! \brief Marks the last event as the descriptor event for the current error context + + Note that marking an event as the descriptor event has the effect + of removing the event from event queue. The event will henceforth + be used as the descriptor for the context. The only effective + fields of a descriptor event are \a short_desc, \a long_desc, \a + facility, \a facility_id and the parameters which are used for + resolving formatted strings in the aforementioned fields. + + Upon calling kherr_set_desc_event(), the event will be + automatically evaluated as if kherr_evaluate_event() was called. + + The event that will be referenced is the last event reported by + the calling thread. + */ +KHMEXP void KHMAPI kherr_set_desc_event(void); +#define _describe kherr_set_desc_event + +/*! \brief Delete the last event + + The event that will be deleted is the last event reported by the + calling thread. + */ +KHMEXP void KHMAPI kherr_del_last_event(void); +#define _del_event kherr_del_last_event + +/*! \brief Create a new context + + The created context is not bound to any thread or any context + hierarchy. Hence it cannot be used to capture any events until it + is used in a call to kherr_push_context(). + + Release the returned context pointer with a call to + kherr_release_context(). + + \param[in] flags Initial flags for the context. Combination of + ::kherr_context_flags + + \note This function is for internal use only. + */ +KHMEXP kherr_context * KHMAPI kherr_create_new_context(khm_int32 flags); + +/*! \brief Obtain a hold on a context */ +KHMEXP void KHMAPI kherr_hold_context(kherr_context * c); + +/*! \brief Release a context */ +KHMEXP void KHMAPI kherr_release_context(kherr_context * c); + +/*! \brief Push an empty context + + Creates an empty context, adds it as a child of the current + thread's error context. If the current thread does not have an + error context, then the created error context will be a root level + context. + + The new context will be the current error context for the calling + thread. + + \param[in] flags Initial flags for the context. Combination of + ::kherr_context_flags + + \see kherr_push_new_context() for more information about thread + specific context stacks. + + */ +KHMEXP void KHMAPI kherr_push_new_context(khm_int32 flags); +#define _begin_task kherr_push_new_context + +/*! \brief Push a context + + Each thread has a stack of error contexts. The topmost one is + current. The thread can push or pop contexts on to the stack + independently of the hierarchy of contexts (the only exception, as + explained below is when the context that is being pushed is + unbound). + + If the context being pushed by kherr_push_context() is unbound, + then it will be attached to the current context of the thread as a + child. Once the new context is pushed to the top of the stack, it + will become the current context for the thread. + + The calling thread must call kherr_pop_context() to remove the + context from the top of the stack. Each call to + kherr_push_new_context() or kher_push_context() must have a + corresponding kherr_pop_context() call. + + When the thread terminates, all of the contexts in the thread's + context stack will be automatically removed. + + \see kherr_pop_context() + */ +KHMEXP void KHMAPI kherr_push_context(kherr_context * c); + +/*! \brief Pop a context + + Remove the current error context from the thread's context stack. + If no other open handles exist to the error context, this causes + the error context to collapse into it's parent context or vanish + entirely unless the context contains an error. + + \see kherr_push_context() for more information about thread + specific context stacks. + */ +KHMEXP void KHMAPI kherr_pop_context(void); +#define _end_task kherr_pop_context + +/*! \brief Retrieve the current error context + + The returned pointer must be released with a call to + kherr_release_context(). +*/ +KHMEXP kherr_context * KHMAPI kherr_peek_context(void); + +/*! \brief Check if the current error context indicates an error + + \return TRUE if there is an error. FALSE otherwise. + \see kherr_analyze() + */ +KHMEXP khm_boolean KHMAPI kherr_is_error(void); + +/*! \brief Check if an error context indicates an error + + \return TRUE if there is an error. FALSE otherwise. + \see kherr_analyze() + */ +KHMEXP khm_boolean KHMAPI kherr_is_error_i(kherr_context * c); + +/*! \brief Clear the error state of the current context */ +KHMEXP void KHMAPI kherr_clear_error(void); + +/*! \brief Clear the error state of an error context */ +KHMEXP void KHMAPI kherr_clear_error_i(kherr_context * c); + +/*! \brief Set the progress meter of the current error context + + Setting \a denom to zero removes the progress meter. + */ +KHMEXP void KHMAPI kherr_set_progress(khm_ui_4 num, khm_ui_4 denom); +#define _progress(num,denom) kherr_set_progress((num),(denom)) + +/*! \brief Get the progress meter of the current error context + */ +KHMEXP void KHMAPI kherr_get_progress(khm_ui_4 * num, khm_ui_4 * denom); + +/*! \brief Get the progress meter of an error context + */ +KHMEXP void KHMAPI kherr_get_progress_i(kherr_context * c, khm_ui_4 * num, khm_ui_4 * denom); + +/*! \brief Get the first event in a context + + The returned pointer is only valid as long as there is a hold on + \a c. Once the context is released with a call to + kherr_release_context() all pointers to events in the context + becomes invalid. + + Use kherr_get_next_event() to obtain the other events. + */ +KHMEXP kherr_event * KHMAPI kherr_get_first_event(kherr_context * c); + +/*! \brief Get the next event + + Call kherr_get_first_event() to obtain the first event in a + context. Subsequent calls to kherr_get_next_event() will yield + other events in the order in which they were reported. The list + ends when kherr_get_next_event() returns NULL. + + The returned pointer is only valid as long as there is a hold on + \a c. Once the context is released with a call to + kherr_release_context() all pointers to events in the context + becomes invalid. + */ +KHMEXP kherr_event * KHMAPI kherr_get_next_event(kherr_event * e); + +/*! \brief Get the first child context of a context + + Contexts are arranged in a hiearchy. This function returns the + first child of an error context. Use kherr_get_next_context() to + obtain the other contexts. If \a c is \a NULL, this returns the + first root level context. + + The returned pointer must be released with a call to + kherr_release_context() + */ +KHMEXP kherr_context * KHMAPI kherr_get_first_context(kherr_context * c); + +/*! \brief Get the next sibling context of a context + + The returned pointer must be released with a call to + kherr_release_context() + + \see kherr_get_first_context() + */ +KHMEXP kherr_context * KHMAPI kherr_get_next_context(kherr_context * c); + +/*! \brief Get the desciption event for the context + + The description event is the event that was denoted using + kherr_set_desc_event() as the event which describes the context. + + The returned pointer is only valid as long as there is a hold on + \a c. Once the context is released with a call to + kherr_release_context() all pointers to events in the context + becomes invalid. + */ +KHMEXP kherr_event * KHMAPI kherr_get_desc_event(kherr_context * c); + +/*! \brief Get the error event for the context + + The error event for a context is the last event that had the + highest severity level. + + The returned pointer is only valid as long as there is a hold on + \a c. Once the context is released with a call to + kherr_release_context() all pointers to events in the context + becomes invalid. + */ +KHMEXP kherr_event * KHMAPI kherr_get_err_event(kherr_context * c); + +/*! \brief Evaluate an event + + When an event is reported, all the parameters and resource + references that were passed to kherr_report() are kept as-is until + the actual string values are required by the error reporting + library. However, if the string fields are required before then, + an application can call kherr_evaluate_event() to get them. + + This function does the following: + + - Load any referenced string or message resources that are + referenced in the event's short description, long description or + suggestion. + + - Expand any inserts using the parameters that were passed in. + + - Free up allocated strings in for the descriptions or suggestion + fields and any parameters. + + - Update the string fields in the event to contain the newly + generated strings. + + */ +KHMEXP void KHMAPI kherr_evaluate_event(kherr_event * e); + +/*! \brief Evaluate the last event + + Same as kherr_evaluate_event(), but operates on the last event + logged by the current thread. + + \see kherr_evaluate_event() + */ +KHMEXP void KHMAPI kherr_evaluate_last_event(void); +#define _resolve kherr_evaluate_last_event + +/*! \defgroup kherr_fids Standard Facility IDs +@{*/ +#define KHM_FACILITY_KMM 1 +#define KHM_FACILITY_KCDB 2 +#define KHM_FACILITY_UI 3 +#define KHM_FACILITY_KRB5 64 +#define KHM_FACILITY_KRB4 65 +#define KHM_FACILITY_AFS 66 +#define KHM_FACILITY_USER 128 +/*@}*/ + +/*@}*/ + +#endif diff --git a/src/windows/identity/kherr/kherrinternal.h b/src/windows/identity/kherr/kherrinternal.h new file mode 100644 index 000000000..e91cc3d01 --- /dev/null +++ b/src/windows/identity/kherr/kherrinternal.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KHERRORINTERNAL_H +#define __KHIMAIRA_KHERRORINTERNAL_H + +#include +#include +#include + +typedef struct tag_kherr_thread { + khm_size nc_ctx; + khm_size n_ctx; + kherr_context ** ctx; +} kherr_thread; + +#define THREAD_STACK_SIZE 8 + +typedef struct tag_kherr_handler_node { + khm_int32 filter; + kherr_ctx_handler h; +} kherr_handler_node; + +#define CTX_ALLOC_INCR 4 + +#define EVENT_MASK_UNRESOLVED \ + (KHERR_RF_RES_SHORT_DESC|KHERR_RF_MSG_SHORT_DESC| \ + KHERR_RF_RES_LONG_DESC|KHERR_RF_MSG_LONG_DESC| \ + KHERR_RF_RES_SUGGEST|KHERR_RF_MSG_SUGGEST) + +extern CRITICAL_SECTION cs_error; +extern DWORD tls_error; +extern kherr_context * ctx_free_list; +extern kherr_event * evt_free_list; +extern kherr_handler_node * ctx_handlers; +extern khm_size n_ctx_handlers; + +#define parm_type(p) ((int) (((p)>>((sizeof(kherr_param) - 1) * 8)) & 0xff)) +#define parm_data(p) ((p) & ~(((kherr_param)0xff)<<((sizeof(kherr_param) - 1) * 8))) + +void resolve_event_strings(kherr_event *); +void attach_this_thread(void); +void detach_this_thread(void); +#endif diff --git a/src/windows/identity/kherr/kherrmain.c b/src/windows/identity/kherr/kherrmain.c new file mode 100644 index 000000000..b108609db --- /dev/null +++ b/src/windows/identity/kherr/kherrmain.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include + +void +kherr_process_attach(void) { + InitializeCriticalSection(&cs_error); + tls_error = TlsAlloc(); +} + +void +kherr_process_detach(void) { + TlsFree(tls_error); + DeleteCriticalSection(&cs_error); +} + +void +kherr_thread_attach(void) { + /* We don't call attach_this_thread() here since we only + want to create a context stack for this thread if + someone wants one. */ + /* attach_this_thread(); */ +} + +void +kherr_thread_detach(void) { + detach_this_thread(); +} diff --git a/src/windows/identity/kmm/Makefile b/src/windows/identity/kmm/Makefile new file mode 100644 index 000000000..6135cdc4f --- /dev/null +++ b/src/windows/identity/kmm/Makefile @@ -0,0 +1,54 @@ +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +MODULE=kmm +!include <../config/Makefile.w32> + +INCFILES= \ + $(INCDIR)\kmm.h \ + $(INCDIR)\kplugin.h + +OBJFILES= \ + $(OBJ)\kmmmain.obj \ + $(OBJ)\kmm.obj \ + $(OBJ)\kmm_plugin.obj \ + $(OBJ)\kmm_module.obj \ + $(OBJ)\kmm_reg.obj \ + $(OBJ)\kmm_registrar.obj \ + $(OBJ)\kmmconfig.obj + +MSGRESFILE=$(OBJ)\kmm_msgs.res + +$(OBJ)\kmmconfig.c: kmmconfig.csv $(CONFDIR)\csvschema.cfg + $(CCSV) $** $@ + +$(MSGRESFILE): $(OBJ)\kmm_msgs.rc + +$(OBJ)\kmm_msgs.rc: lang\kmm_msgs.mc + $(MC2RC) + +all: mkdirs $(INCFILES) $(MSGRESFILE) $(OBJFILES) + +clean:: + $(RM) $(INCFILES) diff --git a/src/windows/identity/kmm/kmm.c b/src/windows/identity/kmm/kmm.c new file mode 100644 index 000000000..af8419fda --- /dev/null +++ b/src/windows/identity/kmm/kmm.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include + +khm_boolean kmm_load_locale_lib(kmm_module_i * m, kmm_module_locale * l) +{ + HMODULE h; + + if(l->filename != NULL) { + h = LoadLibrary(l->filename); + if(!h) + return FALSE; + + EnterCriticalSection(&cs_kmm); + m->h_resource = h; + m->lcid_resource = l->language; + LeaveCriticalSection(&cs_kmm); + + return TRUE; + } else { + /* in this case, the language resources are assumed to be in the + main module library itself. */ + + EnterCriticalSection(&cs_kmm); + m->h_resource = m->h_module; + m->lcid_resource = l->language; + LeaveCriticalSection(&cs_kmm); + + return TRUE; + } +} + + +KHMEXP khm_int32 KHMAPI kmm_set_locale_info(kmm_module module, kmm_module_locale * locales, khm_int32 n_locales) +{ + kmm_module_i * m; + LANGID lcid; + int i; + int * f; + khm_int32 rv = KHM_ERROR_SUCCESS; + + m = kmm_module_from_handle(module); + + if(!m || m->state != KMM_MODULE_STATE_INIT) + return KHM_ERROR_INVALID_OPERATION; + + if(!locales || n_locales < 0) + return KHM_ERROR_INVALID_PARM; + + f = malloc(n_locales * sizeof(int)); + if(!f) + return KHM_ERROR_UNKNOWN; + ZeroMemory(f, sizeof(int) * n_locales); + + lcid = GetUserDefaultLangID(); + + /* first search for an exact match */ + for(i=0; ih_resource; +} +#endif + +KHMEXP kmm_module KHMAPI +kmm_this_module(void) { + kmm_plugin_i * p; + kmm_module_i * m; + kmm_module vm; + + p = TlsGetValue(tls_kmm); + if (!kmm_is_plugin(p)) + return NULL; + + m = p->module; + vm = kmm_handle_from_module(m); + + kmm_hold_module(vm); + + return vm; +} + +KHMEXP kmm_plugin KHMAPI +kmm_this_plugin(void) { + kmm_plugin_i * p; + kmm_plugin vp; + + p = TlsGetValue(tls_kmm); + if (!kmm_is_plugin(p)) + return NULL; + + vp = kmm_handle_from_plugin(p); + + kmm_hold_plugin(vp); + + return vp; +} diff --git a/src/windows/identity/kmm/kmm.h b/src/windows/identity/kmm/kmm.h new file mode 100644 index 000000000..8e487be73 --- /dev/null +++ b/src/windows/identity/kmm/kmm.h @@ -0,0 +1,1010 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KMM_H +#define __KHIMAIRA_KMM_H + +#include +#include + +/*! \defgroup kmm NetIDMgr Module Manager +@{*/ + +/*! \brief A handle to a module. +*/ +typedef khm_handle kmm_module; + +/*! \brief A handle to a plugin. + */ +typedef khm_handle kmm_plugin; + +/*! \name Limits + @{*/ + +/*! \brief Maximum number of characters in a name in KMM including the terminating NULL */ +#define KMM_MAXCCH_NAME 256 + +/*! \brief Maximum number of bytes in a name in KMM including the terminating NULL */ +#define KMM_MAXCB_NAME (sizeof(wchar_t) * KMM_MAXCCH_NAME) + +/*! \brief Maximum number of characters in a description in KMM including the terminating NULL */ +#define KMM_MAXCCH_DESC 512 + +/*! \brief Maximum number of bytes in a description in KMM including the terminating NULL */ +#define KMM_MAXCB_DESC (sizeof(wchar_t) * KMM_MAXCB_NAME) + +/*! \brief Maximum number of dependencies per plugin +*/ +#define KMM_MAX_DEPENDENCIES 8 + +/*! \brief Maximum number of dependants per plugin + */ +#define KMM_MAX_DEPENDANTS 16 + +/*! \brief Maximum number of characters a dependency string including trailing double NULL */ +#define KMM_MAXCCH_DEPS (KMM_MAXCCH_NAME * KMM_MAX_DEPENDENCIES + 1) + +/*! \brief Maximum number of bytes in a dependency string including trailing double NULL */ +#define KMM_MAXCB_DEPS (sizeof(wchar_t) * KMM_MAXCCH_DEPS) +/*@}*/ /* Limits */ + +/*! \brief Plugin registration + + \see ::khm_cred_provider +*/ +typedef struct tag_kmm_plugin_reg { + wchar_t * name; /*!< Name of the plugin. Maximum of + KMM_MAXCCH_NAME characters + including the terminating + NULL. Required. */ + + wchar_t * module; /*!< Name of module that owns the + plugin. Maximum of + KMM_MAXCCH_NAME characters + including terminating NULL. + Required. */ + + khm_int32 type; /*!< Type plugin type. One of + KHM_PITYPE_*. Required. */ + khm_int32 flags; /*!< Unused. Set to 0 */ + kmq_callback_t msg_proc; /*!< Message processor. Required. */ + wchar_t * dependencies; /*!< Dependencies. Note that this is + a multi string. (you can use the + KHC multi string functions to + manipulate multi strings or to + convert a comma separated list of + dependencies to a multi string). + Each string in the multi string + is a name of a plugin that this + plugin depends on. Optional (set + to NULL if this plugin has no + dependencies). Maximum of + KMM_MAXCCH_DEPS characters + including terminating double + NULL.*/ + + wchar_t * description; /*!< Description of the plugin. + Maximum of KMM_MAXCCH_DESC + characters including the + terminating + NULL. Localized. Optional (set to + NULL if not provided) */ +#ifdef _WIN32 + HICON icon; /*!< Icon used to represent the + plugin. Optional. (set to NULL if + not provided) */ +#endif +} kmm_plugin_reg; + +/*! \brief Plugin information +*/ +typedef struct tag_kmm_plugin_info { + kmm_plugin_reg reg; /*!< Registration info */ + + khm_int32 state; /*!< Current status of the plugin. + One of ::_kmm_plugin_states */ + + khm_int32 failure_count; /*!< Number of recorded failures in + the plugin */ + FILETIME failure_time; /*!< Time of first recorded failure */ + khm_int32 failure_reason; /*!< The reason for the first recorded + failure */ + + kmm_plugin h_plugin; /*!< Handle to plugin */ +} kmm_plugin_info; + +/*! \name Plugin types +@{*/ +/*! \brief A credentials provider + + \see \ref pi_pt_cred for more information. +*/ +#define KHM_PITYPE_CRED 1 + +/*! \brief A configuration provider + + \see \ref pi_pt_conf for more information. +*/ +#define KHM_PITYPE_CONFIG 2 + +/*@}*/ + +/*! \name Plugin flags +@{*/ + +/*! \brief The plugin is an identity provider +*/ +#define KHM_PIFLAG_IDENTITY_PROVIDER 1 +/*@}*/ + +/*! \brief Plugin states */ +enum _kmm_plugin_states { + KMM_PLUGIN_STATE_FAIL_UNKNOWN = -5, /*!< Failed due to unknown + reasons */ + KMM_PLUGIN_STATE_FAIL_MAX_FAILURE = -4, /*!< The plugin has + reached the maximum number + of failures and cannot be + initialized until the + failure count is reset */ + KMM_PLUGIN_STATE_FAIL_NOT_REGISTERED = -3, /*!< Failed because the + plugin was not registered + and automatic registration + failed. */ + KMM_PLUGIN_STATE_FAIL_DISABLED = -2,/*!< Failed because plugin was + disabled by the user. */ + KMM_PLUGIN_STATE_FAIL_LOAD = -1, /*!< The plugin failed to load + due to some unknown + reason. */ + KMM_PLUGIN_STATE_NONE = 0, /*!< Unknown state */ + KMM_PLUGIN_STATE_PLACEHOLDER, /*!< Placeholder. The plugin + hasn't been provided by + anyone yet, but the plugin + record has been created to + keep track of + dependencies. */ + KMM_PLUGIN_STATE_REG, /*!< The plugin is registered + but not initialized */ + KMM_PLUGIN_STATE_PREINIT, /*!< The plugin is in the + process of being + initialized */ + KMM_PLUGIN_STATE_HOLD, /*!< On hold. One or more + dependencies of this plugin + has not been resolved */ + KMM_PLUGIN_STATE_INIT, /*!< The plugin was initialized */ + KMM_PLUGIN_STATE_RUNNING, /*!< The plugin is running */ + KMM_PLUGIN_STATE_EXITED /*!< The plugin has been stopped. */ +}; + +/*! \brief Module registration */ +typedef struct tag_kmm_module_reg { + wchar_t * name; /*!< Identifier for the module */ + wchar_t * path; /*!< Full pathname to module + binary */ + + wchar_t * description; /*!< Description of module */ + + wchar_t * vendor; /*!< Vendor/copyright string */ + + khm_int32 n_plugins; /*!< Number of plugins that are + active */ + kmm_plugin_reg * plugin_reg_info; /*!< Array of kmm_plugin_reg + records for each active + plugin */ +} kmm_module_reg; + +/*! \brief Module information record */ +typedef struct tag_kmm_module_info { + kmm_module_reg reg; /*!< Registration info */ + + khm_ui_4 language; /*!< Currently loaded langugage */ + + khm_int32 state; /*!< Current status of the + module */ + + khm_version file_version; /*!< File version for the + module */ + khm_version product_version; /*!< Product version for the + module */ + + khm_int32 failure_count; /*!< Number of times the module + has failed to load */ + FILETIME failure_time; /*!< Time of first recorded + failure */ + khm_int32 failure_reason; /*!< Reason for first failure. + One of the module status + values */ + + kmm_module h_module; /*!< Handle to the module. */ +} kmm_module_info; + +/*! \brief Module states +*/ +enum KMM_MODULE_STATES { + KMM_MODULE_STATE_FAIL_UNKNOWN=-10, /*!< Module could not be + loaded due to unknown + reasons. */ + KMM_MODULE_STATE_FAIL_MAX_FAILURE=-9,/*!< The module has failed + too many times already. Not + attempting to restart it + again */ + KMM_MODULE_STATE_FAIL_DUPLICATE=-8, /*!< An attempt was made to + load the same module + twice. */ + KMM_MODULE_STATE_FAIL_NOT_REGISTERED=-7, /*!< The module is not + found among the registered + module list */ + KMM_MODULE_STATE_FAIL_NO_PLUGINS=-6,/*!< The module provided no + plugins, or all the plugins + that are provided are + disabled */ + KMM_MODULE_STATE_FAIL_DISABLED=-5, /*!< Module is disabled and + cannot be loaded */ + KMM_MODULE_STATE_FAIL_LOAD=-4, /*!< The module failed to + initialize */ + KMM_MODULE_STATE_FAIL_INVALID=-3, /*!< The module was invalid. + Typically caused by the + required entrypoints not + being present */ + KMM_MODULE_STATE_FAIL_SIGNATURE=-2, /*!< The module failed to load + due to an unverifiable + signature */ + KMM_MODULE_STATE_FAIL_NOT_FOUND=-1, /*!< The module was not + found */ + KMM_MODULE_STATE_NONE=0, /*!< Unknown state. The handle + is possibly invalid */ + KMM_MODULE_STATE_PREINIT, /*!< The module is being + loaded. init_module() hasn't + been called yet */ + KMM_MODULE_STATE_INIT, /*!< In init_module() */ + KMM_MODULE_STATE_INITPLUG, /*!< Initializing plugins */ + KMM_MODULE_STATE_RUNNING, /*!< Running */ + KMM_MODULE_STATE_EXITPLUG, /*!< Currently exiting plugins */ + KMM_MODULE_STATE_EXIT, /*!< Currently exiting */ + KMM_MODULE_STATE_EXITED /*!< Exited */ +}; + +/*! \brief Start the Module Manager + + \note Only called by the NetIDMgr core. +*/ +KHMEXP void KHMAPI +kmm_init(void); + +/*! \brief Stop the Module Manager + + \note Only called by the NetIDMgr core. +*/ +KHMEXP void KHMAPI +kmm_exit(void); + +/*! \brief Return the plugin handle for the current plugin + + The returned handle represents the plugin which owns the current + thread. The returned handle must be released by calling + kmm_release_plugin(). Returns NULL if the current thread is not + owned by any plugin. + */ +KHMEXP kmm_plugin KHMAPI +kmm_this_plugin(void); + +/*! \brief Return the module handle for the current module + + The returned handle represents the module which owns the current + thread. The returned handle must be released by calling + kmm_release_module() +*/ +KHMEXP kmm_module KHMAPI +kmm_this_module(void); + +/*! \name Flags for kmm_load_module() +@{*/ +/*!\brief Load synchronously + + If this flag is set, then the function waits for the module to be + loaded. The default is to load the module asynchronously. + + When loading a module asynchronously, the kmm_load_module() + function returns KHM_ERROR_SUCCESS and exits without waiting for + the module to load. If \a result is not NULL, it will receive a + valid handle to the module. + + When loading a module synchronously, kmm_load_module() will wait + for the module to completely load. If it fails to load properly, + it will return an error code and set \a result to NULL. +*/ +#define KMM_LM_FLAG_SYNC 1 + +/*! \brief Do not load + + Indicates that the module shouldn't actually be loaded. If the + specified module name identifies a module that has already been + loaded, then the function returns a held handle to the existing + module (use kmm_release_module() to free the handle). Otherwise, + the function returns KHM_ERROR_NOT_FOUND. +*/ +#define KMM_LM_FLAG_NOLOAD 2 +/*@}*/ + +/*! \brief Load a module + + The \a modulename parameter specifies a module to load. Depending + on the configuration, not all of the plugins that are provided by + the module may be loaded. If no plugins are successfully loaded, + the module will be immediately unloaded. + + If the module is currently loaded or is being loaded, then a valid + handle to the existing module is returned. + + When called with KMM_LM_FLAG_SYNC, the function does not return + until the module and the associated plugins are all initialized, + or an error occurs. + + If the KMM_LM_FLAG_NOLOAD flag is set, then a handle to an + existing instance of the module will be returned. If the module + hasn't been loaded yet, then no handle is returned and the + function returns KHM_ERROR_NOT_FOUND. + + See the associated NetIDMgr Module Manager documentation on the + sequence of events associated with loading a module. + + \param[in] modulename Name of the module. The module should have + been registered under this name prior to the call. + \param[in] flags Combination of KMM_LM_FLAG_* + \param[out] result Receives a handle to the loaded module. If the + result is not required, set this to NULL. If \a result is not + NULL, and km_load_module() returns KHM_ERROR_SUCCESS, then + kmm_release_module() must be called to release the handle to + the module. Otherwise, \a result receives NULL. If a handle + is returned, it will be valid regardless of whether the module + fails to load or not. You can use kmm_get_module_state() to + query the progress of the loading process. See + ::KMM_LM_FLAG_SYNC. + + \retval KHM_ERROR_SUCCESS The call succeeded. If \a + KMM_LM_FLAG_SYNC was specified, this means that the module was + successfully loaded. Otherwise, it only means that the module + has been queued up for loading. Use kmm_get_module_state() to + determine if it was successfully loaded. If \a result is not + NULL, a valid handle is returned. + \retval KHM_ERROR_EXISTS The module is already loaded or has been + already queued for loading. If \a result is not NULL, a valid + handle to the existing module instance is returned. + \retval KHM_ERROR_NOT_FOUND If called with KMM_LM_FLAG_NOLOAD, + indicates that the module has not been loaded. Otherwise only + returned when called with KMM_LM_FLAG_SYNC. The module image + was not found. No handle is returned. + \retval KHM_ERROR_INVALID_SIGNATURE Only returned when called with + KMM_LM_FLAG_SYNC. The module was signed with an invalid + certificate. No handle is returned. + \retval KHM_ERROR_UNKNOWN Only returned when called with + KMM_LM_FLAG_SYNC. Some other error has occured. No handle is + returned. + + \see \ref pi_fw_pm_load + \see ::KMM_LM_FLAG_SYNC, ::KMM_LM_FLAG_NOLOAD +*/ +KHMEXP khm_int32 KHMAPI +kmm_load_module(wchar_t * modname, khm_int32 flags, kmm_module * result); + +/*! \brief Hold a handle to a module + + Use kmm_release_module() to release the hold. +*/ +KHMEXP khm_int32 KHMAPI +kmm_hold_module(kmm_module module); + +/*! \brief Release a handle to a module + + Release a held referece to a module that was returned in a call to + kmm_load_module(). +*/ +KHMEXP khm_int32 KHMAPI +kmm_release_module(kmm_module m); + +/*! \brief Query the state of a module + + When loading a module asynchronously you can query the state of + the loading process using this. The return value is a status + indicator. + + \return The return value is one of the ::KMM_MODULE_STATES + enumerations. +*/ +KHMEXP khm_int32 KHMAPI +kmm_get_module_state(kmm_module m); + +/*! \brief Unload a module + + See the associated NetIDMgr Module Manager documentation on the + sequence of events associated with unloading a module. + + \see \ref pi_fw_pm_unload +*/ +KHMEXP khm_int32 KHMAPI +kmm_unload_module(kmm_module module); + +/*! \brief Loads the default modules as specified in the configuration + + The configuration can specify the default set of modules to load. + This function dispatches the necessary message for loading these + modules and reutnrs. +*/ +KHMEXP khm_int32 KHMAPI +kmm_load_default_modules(void); + +/*! \brief Checks whether there are any pending loads + + Returns TRUE if there are modules still waiting to be loaded. +*/ +KHMEXP khm_boolean KHMAPI +kmm_load_pending(void); + +#ifdef _WIN32 +/*! \brief Returns the Windows module handle from a handle to a NetIDMgr module. + + Although it is possible to obtain the Windows module handle and + use it to call Windows API functions, it is not recommended to do + so. This is because that might cause the state of the module to + change in ways which are inconsistent from the internal data + structures that kmm maintains. +*/ +KHMEXP HMODULE KHMAPI +kmm_get_hmodule(kmm_module m); +#endif + +/*! \brief Hold a plugin + + Obtains a hold on a plugin. The plugin handle will remain valid + until the hold is released with a call to kmm_release_plugin(). + No guarantees are made on the handle once the handle is released. + */ +KHMEXP khm_int32 KHMAPI +kmm_hold_plugin(kmm_plugin p); + +/*! \brief Release a plugin + + Releases a hold on a plugin obtained through a call to + kmm_hold_plugin(). The plugin handle should no longer be + considered valied once this is called. + */ +KHMEXP khm_int32 KHMAPI +kmm_release_plugin(kmm_plugin p); + +/*! \brief Provide a plugin + + This function must be called for each plugin that the module + provides. + + Note that this function returns immediately and does not + initialize the plugin. All plugins that are provided by a + module will be initialized once the init_module() function + returns. If the plugin has dependencies, it will be kept in a + held state until the plugins that it depends on are successfully + initialized. If the dependencies are not resolved (the dependent + plugins are not loaded), then plugin will not be initialized. + + If the plugin is not registered and \a plugin contains enough + information to perform the registration, then it will be + automatically registered. However, if the plugin is not + registered and cannot be registered using the provided + information, the plugin will not be initialized properly. Note + that automatic registration will always register the plugin in the + user configuration store. + + The \a name and \a msg_proc members of \a plugin are required to + have valid values. The \a icon member may optionally be + specified. The other fields can be specified if the plugin should + be automatically registered, however, the \a module field will be + ignored and will be determined by the \a module handle. + + \param[in] module Handle to this module that is providing the plugin. + \param[in] plugin A plugin descriptor. + + \retval KHM_ERROR_SUCCESS Succeeded. + \retval KHM_ERROR_INVALID_OPERATION The function was not called + during init_module() + \retval KHM_ERROR_INVALID_PARM One or more parameters were invalid + \retval KHM_ERROR_DUPLICATE The plugin was already provided + + \note This can only be called when handing init_module() +*/ +KHMEXP khm_int32 KHMAPI +kmm_provide_plugin(kmm_module module, kmm_plugin_reg * plugin); + +/*! \brief Query the state of a plugin. + + \return One of ::_kmm_plugin_states +*/ +KHMEXP khm_int32 KHMAPI +kmm_get_plugin_state(wchar_t * plugin); + +/*! \defgroup kmm_reg Registration + + The functions for managing plugin and module registration. These + functions are also available as static linked libraries for use by + external applications which must register or unregister plugins or + modules. +@{*/ + +/*! \brief Obtain the configuration space for a named plugin + + Note that the named plugin does not have to actually exist. + Configuration spaces for plugins are based solely on the plugin + name and hence can be accessed regardless of whether the specific + plugin is loaded or not. + + \param[in] flags Controls the options for opening the + configuration space. If KHM_FLAG_CREATE is specified, then + the configuration space for the plugin named \a plugin wil be + created if it doesn't already exist. The \a flags parameter + is directly passed into a call to khc_open_space(). + + \param[in] plugin Name of the plugin. The name can not contain + slashes. + + \param[out] result Receives a configuration space handle. The + calling application should free the handle using + khc_close_space(). + + \see khc_open_space() + \see khc_close_space() + */ +KHMEXP khm_int32 KHMAPI +kmm_get_plugin_config(wchar_t * plugin, khm_int32 flags, khm_handle * result); + +/*! \brief Obtain the configuration space or a named module + + The named module does not have to actually exist. Configuration + spaces for modules are based on the basename of the module + (including the extension). + + \param[in] module Name of the module. + + \param[in] flags The flags used to call khc_open_space(). You can + use this to specify a particular configuration store if + needed. + + \param[out] result Receives the handle to a configuration space if + successful. Call khc_close_space() to close the handle. + + \see khc_open_space() + \see khc_close_space() +*/ +KHMEXP khm_int32 KHMAPI +kmm_get_module_config(wchar_t * module, khm_int32 flags, khm_handle * result); + +/*! \brief Retrieve a handle to the configuration space for plugins + + The configuration space for plugins is a container which holds the + configuration subspaces for all the plugins. This is the config + space which must be used to load a configuration space for a + plugin. + + \param[in] flags The flags to pass in to the call to + khc_open_space(). The flags can be used to select a specific + configuration store if needed. + + \param[out] result Receives a handle to the configuration + space. Call khc_close_space() to close the handle + + \see khc_open_space() + \see khc_close_space() + */ +KHMEXP khm_int32 KHMAPI +kmm_get_plugins_config(khm_int32 flags, khm_handle * result); + +/*! \brief Retrieve the handle to the configuration space for modules + + The configuration space for modules is a container which hold the + configuration subspaces for all the modules. Each module + registration ends up in this subspace. + + \param[in] flags The flags to pass in to the call to + khc_open_space(). The flags can be used to select a specific + configuration store if needed. + + \param[out] result Receives a handle to the configuration space. + Call khc_close_space() to close the handle. + + \see khc_open_space() + \see khc_close_space() + */ +KHMEXP khm_int32 KHMAPI +kmm_get_modules_config(khm_int32 flags, khm_handle * result); + +/*! \brief Return information about a loaded module + + The retrieves a block of information about a module. Refer to + ::kmm_module_info for information about the format of the returned + data. + + Note that the size of the required buffer is actually greater than + the size of the ::kmm_module_info structure and accomodates the + ::kmm_plugin_info structures and strings required to complete the + information block. + + Call the function with \a buffer set to NULL and \a cb_buffer + pointing at a khm_size variable to obtain the required size of the + buffer. + + \param[in] module_name Name of a module + \param[in] flags Flags indicating which types of information to + return + \param[out] buffer Points to a buffer that recieves information. + Set this to NULL if only the size of the buffer is required. + \param[in,out] On entry, contains the size of the buffer pointed + to by \a buffer if \a buffer is not NULL. On exit, contains + the required size of the buffer or the number of actual bytes + copied. + + \retval KHM_ERROR_SUCCESS The requested information was copied + \retval KHM_ERROR_INVALID_PARM One of the parameters was invalid + \retval KHM_ERROR_TOO_LONG The buffer was not large enough or was + NULL. The number of bytes requied is in \a cb_buffer. + \retval KHM_ERROR_NOT_FOUND The specified module is not a + registered module. + */ +KHMEXP khm_int32 KHMAPI +kmm_get_module_info(wchar_t * module_name, khm_int32 flags, + kmm_module_info * buffer, khm_size * cb_buffer); + +/*! \brief Get information about a module + + Similar to kmm_get_module_info(), but uses a module handle instead + of a name, and uses internal buffers for providing string fields. + + The information that is returned should be freed using a call to + kmm_release_module_info_i(). + + \see kmm_release_module_info_i() + */ +KHMEXP khm_int32 KHMAPI +kmm_get_module_info_i(kmm_module module, kmm_module_info * info); + +/*! \brief Release module information + + Releases the information returned by a previous call to + kmm_get_module_info_i(). The contents of the ::kmm_module_info + structure should not have been modified in any way between calling + kmm_get_module_info_i() and kmm_release_module_info_i(). + */ +KHMEXP khm_int32 KHMAPI +kmm_release_module_info_i(kmm_module_info * info); + +/*! \brief Obtain information about a plugin + + Retrieve a block of information about a plugin. See + ::kmm_plugin_info for details about what information can be + returned. Note that some fields may not be available if the + module is not loaded. + + Note that the size of the required buffer is greater than the size + of the ::kmm_plugin_info structure and accounts for strings as + well. Call kmm_get_plugin_info() with \a buffer set to NULL and + \a cb_buffer set to point to a variable of type \a khm_size to + obtain the required size of the structure. + + \param[in] plugin_name Name of the plugin + \param[out] buffer The buffer to receive the plugin information. + Set to \a NULL if only the size of the buffer is required. + \param[in,out] cb_buffer On entry, points to variable that + specifies the size of the buffer pointed to by \a buffer is \a + buffer is not \a NULL. On exit, holds the number of bytes + copied or the required size of the buffer. + + \retval KHM_ERROR_SUCCESS The requested information was + successfully copied to the \a buffer + \retval KHM_ERROR_TOO_LONG The buffer was either \a NULL or + insufficient to hold the requested information. The required + size of the buffer was stored in \a cb_buffer + \retval KHM_ERROR_INVALID_PARM One or more parameters were + invlaid. + \retval KHM_ERROR_NOT_FOUND The specified plugin was not found + among the registered plugins. +*/ +KHMEXP khm_int32 KHMAPI +kmm_get_plugin_info(wchar_t * plugin_name, + kmm_plugin_info * buffer, + khm_size * cb_buffer); + +/*! \brief Obtain information about a plugin using a plugin handle + + Similar to kmm_get_plugin_info() but uses a plugin handle instead + of a plugin name. If the call is successful, the \a info + structure will be filled with information about the plugin. The + returned info should not be modified in any way and may contain + pointers to internal buffers. + + The returned information must be released with a call to + kmm_release_plugin_info_i(). + */ +KHMEXP khm_int32 KHMAPI +kmm_get_plugin_info_i(kmm_plugin p, kmm_plugin_info * info); + +/*! \brief Release plugin information returned by kmm_get_plugin_info_i + + The information returned by kmm_get_plugin_info_i() should not be + modified in any way before calling kmm_release_plugin_info_i(). + Once the call completes, the contents of \a info will be + initialized to zero. + */ +KHMEXP khm_int32 KHMAPI +kmm_release_plugin_info_i(kmm_plugin_info * info); + +/*! \brief Enumerates plugins + + Enumerates through known plugins. This list may not include + plugins which were not loaded by NetIDMgr in this session. + + If the call is successful, a handle to the next plugin in the list + will be placed in \a p_next. The returned handle must be freed + with a call to kmm_release_plugin(). + + If the \a p parameter is set to NULL, then the first plugin handle + will be placed in \a p_next. The handles will not be returned in + any specific order. In addition, the enumeration may not include + all known plugins if the list of plugins changes during + enumeration. + */ +KHMEXP khm_int32 KHMAPI +kmm_get_next_plugin(kmm_plugin p, kmm_plugin * p_next); + +/*! \brief Register a plugin + + The \a plugin member defines the plugin to be registered. The \a + msg_proc and \a icon members of the structure are ignored. + + At the time kmm_register_plugin() is called, the module specified + by \a module member of the \a plugin parameter must have been already + registered. Otherwise the function call fails. + + If the plugin has already been registered, then all the fields in + the plugin registration will be updated to be in sync with the + information provided in the \a plugin parameter. The failure + counts and associated statistics will not be reset when the + configuration information is updated. + + If the plugin has not been registered, the a new registration + entry is created in the configuration space indicated by the \a + config_flags parameter. In addition, the plugin will be added to + the list of plugins associated with the owning module. + + Note that the module that owns the plugin must be registered in + the same configuration store as the plugin. + + \param[in] plugin Registration info for the plugin. The \a + msg_proc and \a icon members are ignored. All other fields + are required. The \a description member should be localized + to the system locale when registering a plugin in the machine + configuration store and should be localized to the user locale + when registering a plugin in the user configuration store. + \param[in] config_flags Flags for the configuration provider. + These flags are used verbatim to call khc_open_space(), hence + they may be used to pick whether or not the registration is + per machine or per user. + + \see kmm_register_module() + */ +KHMEXP khm_int32 KHMAPI +kmm_register_plugin(kmm_plugin_reg * plugin, khm_int32 config_flags); + +/*! \brief Register a module + + The \a module parameter specifies the parameters for the module + registration. + + The \a plugin_info member should point to an array of + ::kmm_plugin_info structures unless the \a n_plugins member is + zero, in which case \a plugin_info can be \a NULL. Plugins can be + registered separately using kmm_register_plugin(). + + \param[in] module Information about the module. All members are + required, however \a plugin_info can be \a NULL if \a + n_plugins is zero. + + \param[in] config_flags Flags used to call khc_open_space(). This + can be used to choose the configuration store in which the + module registration will be performed. + */ +KHMEXP khm_int32 KHMAPI +kmm_register_module(kmm_module_reg * module, khm_int32 config_flags); + +/*! \brief Unregister a plugin + + Registration information associated with the plugin will be + removed. In addtion, the plugin will be removed from the list of + plugins provided by the owner module. + + \param[in] plugin Names the plugin to be removed + \param[in] config_flags Flags used to call khc_open_space(). Can + be used to choose the configuraiton store that is affected by + the call. + + \note kmm_unregister_plugin() has no effect on whether the plugin + is loaded or not. The caller must make sure that the plugin + is unloaded and the associated module is either also unloaded + or in a state where the plugin can be unregistered. + */ +KHMEXP khm_int32 KHMAPI +kmm_unregister_plugin(wchar_t * plugin, khm_int32 config_flags); + +/*! \brief Unregister a module + + Registration information associated with the module as well as all + the plugins provided by the module will be removed from the + configuration store. + + \param[in] module Names the module to be removed + + \param[in] config_flags Flags used to call khc_open_space(). Can + be used to choose the configuration store affected by the + call. + + \note kmm_unregister_module() has no effect on the loaded state of + the module. The caller should make sure that the module is + unloaded and in a state where it can be unregistered. + */ +KHMEXP khm_int32 KHMAPI +kmm_unregister_module(wchar_t * module, khm_int32 config_flags); + +/*@}*/ /* kmm_reg */ + +/*! \defgroup kmm_loc Internationalization support + + See \ref pi_localization for more information about + internationalization. + +@{*/ + +/*! \brief Locale descriptor record + + See kmm_set_locale() +*/ +typedef struct tag_kmm_module_locale { + khm_ui_4 language; /*!< A language ID. On Windows, you can use the + MAKELANGID macro to generate this value. */ + wchar_t * filename; /*!< The filename corresponding to this language. + Use NULL to indicate that resources for this + language are to be found in the main module. */ + khm_int32 flags; /*!< Flags. Combination of KMM_MLOC_FLAG_* */ +} kmm_module_locale; + +#define LOCALE_DEF(language_id, filename, flags) {language_id, filename, flags} + +/*! \brief Default (fallback) locale +*/ +#define KMM_MLOC_FLAG_DEFAULT 1 + + +/*! \brief Sets the locale for a loaded module. + + The given locale records are searched in the given order until a + locale that matches the current user locale is found. If no + locales match, then the first locale with the + ::KMM_MLOC_FLAG_DEFAULT flag set will be loaded. If no locales + have that flag set, then the first locale is loaded. + + You can obtain a handle to the loaded library using + kmm_get_resource_hmodule(). This function does not return until a + matched library is loaded. + + \param[in] module The module handle + \param[in] locales An array of ::kmm_module_locale objects + \param[in] n_locales The number of objects in the array pointed to by \a locales + + \retval KHM_ERROR_SUCCESS Succeeded. + \retval KHM_ERROR_NOT_FOUND A matching locale resource library was not found. + \retval KHM_ERROR_INVALID_OPERATION The function was called on a module which is currently not being initalized. + + \see \ref pi_localization + \see kmm_get_resource_hmodule() + + \note This can only be called when handing init_module() +*/ +KHMEXP khm_int32 KHMAPI +kmm_set_locale_info(kmm_module module, + kmm_module_locale * locales, + khm_int32 n_locales); + +#ifdef _WIN32 + +/*! \brief Return the Windows module handle of the resource library of a NetIDMgr module. + + NetIDMgr allows the specification of an alternate resource library + that will be used to load localized resources from. This function + returns a handle to this library. + + While you can use the convenience macros to access resources in a + localization library using the module handle, it is recommended, + for performance reasons, to use this function to obtain the handle + to the resource library and then use that handle in calls to + LoadString, LoadImage etc. directly. +*/ +KHMEXP HMODULE KHMAPI +kmm_get_resource_hmodule(kmm_module m); + +/*! \name Convenience Macros +@{*/ +/*! \brief Convenience macro for using calling LoadAccelerators using a module handle + + \param[in] module A handle to a loaded module. The corresponding resource + module will be located through a call to kmm_get_resource_hmodule() +*/ +#define kmm_LoadAccelerators(module, lpTableName) \ + (LoadAccelerators(kmm_get_resource_hmodule(module), lpTableName)) + +/*! \brief Convenience macro for using calling LoadBitmap using a module handle + + \param[in] module A handle to a loaded module. The corresponding resource + module will be located through a call to kmm_get_resource_hmodule() +*/ +#define kmm_LoadBitmap(module, lpBitmapName) \ + (LoadBitmap(kmm_get_resource_hmodule(module), lpBitmapName)) + +/*! \brief Convenience macro for using calling LoadImage using a module handle + + \param[in] module A handle to a loaded module. The corresponding resource + module will be located through a call to kmm_get_resource_hmodule() +*/ +#define kmm_LoadImage(module, lpszName, uType, cxDesired, cyDesired, fuLoad) \ + (LoadImage(kmm_get_resource_hmodule(module), lpszName, uType, cxDesired, cyDesired, fuLoad)) + +/*! \brief Convenience macro for using calling LoadCursor using a module handle + + \param[in] module A handle to a loaded module. The corresponding resource + module will be located through a call to kmm_get_resource_hmodule() +*/ +#define kmm_LoadCursor(module, lpCursorName) \ + (LoadCursor(kmm_get_resource_hmodule(module), lpCursorName)) + +/*! \brief Convenience macro for using calling LoadIcon using a module handle + + \param[in] module A handle to a loaded module. The corresponding resource + module will be located through a call to kmm_get_resource_hmodule() +*/ +#define kmm_LoadIcon(module, lpIconName) \ + (LoadIcon(kmm_get_resource_hmodule(module), lpIconName)) + +/*! \brief Convenience macro for using calling LoadMenu using a module handle + + \param[in] module A handle to a loaded module. The corresponding resource + module will be located through a call to kmm_get_resource_hmodule() +*/ +#define kmm_LoadMenu(module, lpMenuName) \ + (LoadMenu(kmm_get_resource_hmodule(module), lpMenuName)) + +/*! \brief Convenience macro for using calling LoadString using a module handle + + \param[in] module A handle to a loaded module. The corresponding resource + module will be located through a call to kmm_get_resource_hmodule() +*/ +#define kmm_LoadString(module, uID, lpBuffer, nBufferMax) \ + (LoadString(kmm_get_resource_hmodule(module), uID, lpBuffer, nBufferMax)) +/*@}*/ /* Convenience Macros */ +#endif +/*@}*/ /* group kmm_loc */ +/*@}*/ /* group kmm */ +#endif diff --git a/src/windows/identity/kmm/kmm_module.c b/src/windows/identity/kmm/kmm_module.c new file mode 100644 index 000000000..e1f5292ce --- /dev/null +++ b/src/windows/identity/kmm/kmm_module.c @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include + +kmm_module_i * kmm_get_module_i(wchar_t * name) +{ + kmm_module_i * m; + size_t sz; + + if(FAILED(StringCbLength(name, KMM_MAXCB_NAME, &sz))) + return NULL; + sz += sizeof(wchar_t); + + EnterCriticalSection(&cs_kmm); + m = (kmm_module_i *) hash_lookup(hash_modules, (void *) name); + + if(m == NULL) { + m = malloc(sizeof(kmm_module_i)); + ZeroMemory(m, sizeof(kmm_module_i)); + + m->magic = KMM_MODULE_MAGIC; + m->name = malloc(sz); + StringCbCopy(m->name, sz, name); + m->state = KMM_MODULE_STATE_NONE; + + hash_add(hash_modules, (void *) m->name, (void *) m); + LPUSH(&kmm_all_modules, m); + } + LeaveCriticalSection(&cs_kmm); + + return m; +} + +kmm_module_i * kmm_find_module_i(wchar_t * name) +{ + kmm_module_i * m; + + EnterCriticalSection(&cs_kmm); + m = (kmm_module_i *) hash_lookup(hash_modules, (void *) name); + LeaveCriticalSection(&cs_kmm); + + return m; +} + +/* called with cs_kmm held */ +void kmm_free_module(kmm_module_i * m) +{ + m->magic = 0; + + hash_del(hash_modules, m->name); + LDELETE(&kmm_all_modules, m); + + if (m->name) + free(m->name); + if (m->path) + free(m->path); + if (m->vendor) + free(m->vendor); + if (m->version_info) + free(m->version_info); + free(m); + + if (kmm_all_modules == NULL) + SetEvent(evt_exit); +} + +KHMEXP khm_int32 KHMAPI kmm_hold_module(kmm_module module) +{ + if(!kmm_is_module(module)) + return KHM_ERROR_INVALID_PARM; + EnterCriticalSection(&cs_kmm); + kmm_module_from_handle(module)->refcount++; + LeaveCriticalSection(&cs_kmm); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kmm_release_module(kmm_module vm) +{ + kmm_module_i * m; + if(!kmm_is_module(vm)) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_kmm); + m = kmm_module_from_handle(vm); + if(! --(m->refcount)) + { + /* note that a 0 ref count means that there are no active + plugins */ + kmm_free_module(m); + } + LeaveCriticalSection(&cs_kmm); + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kmm_load_module(wchar_t * modname, + khm_int32 flags, + kmm_module * result) +{ + kmm_module_i * m = NULL; + kmm_module_i * mi; + size_t cbsize; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(FAILED(StringCbLength(modname, KMM_MAXCB_NAME, &cbsize))) + return KHM_ERROR_INVALID_PARM; + cbsize += sizeof(wchar_t); + + EnterCriticalSection(&cs_kmm); + mi = kmm_find_module_i(modname); + + if(mi != NULL) { + kmm_hold_module(kmm_handle_from_module(mi)); + /* check if the module has either failed to load either or if + it has been terminated. If so, we try once again to load the + module. */ + if(!(flags & KMM_LM_FLAG_NOLOAD) && + (mi->state < 0 || mi->state == KMM_MODULE_STATE_EXITED)) + { + mi->state = KMM_MODULE_STATE_PREINIT; + } + } + LeaveCriticalSection(&cs_kmm); + + if(flags & KMM_LM_FLAG_NOLOAD) { + if(result) + *result = mi; + else if(mi) + kmm_release_module(kmm_handle_from_module(mi)); + + return (mi)? KHM_ERROR_SUCCESS: KHM_ERROR_NOT_FOUND; + } + + if(mi) { + m = mi; + } else { + m = kmm_get_module_i(modname); + m->state = KMM_MODULE_STATE_PREINIT; + kmm_hold_module(kmm_handle_from_module(m)); + } + + /* the module is already running or is already being + worked on by the registrar */ + if(m->state != KMM_MODULE_STATE_PREINIT) { + if(result) + *result = kmm_handle_from_module(m); + else + kmm_release_module(kmm_handle_from_module(m)); + + return KHM_ERROR_EXISTS; + } + + kmmint_add_to_module_queue(); + + if(flags & KMM_LM_FLAG_SYNC) { + kmm_hold_module(kmm_handle_from_module(m)); + kmq_send_message(KMSG_KMM, + KMSG_KMM_I_REG, + KMM_REG_INIT_MODULE, + (void*) m); + if(m->state <= 0) { + /* failed to load ? */ + if(m->state == KMM_MODULE_STATE_FAIL_NOT_FOUND) + rv = KHM_ERROR_NOT_FOUND; + else if(m->state == KMM_MODULE_STATE_FAIL_SIGNATURE) + rv = KHM_ERROR_INVALID_SIGNATURE; + else + rv = KHM_ERROR_UNKNOWN; + + kmm_release_module(kmm_handle_from_module(m)); + if(result) + *result = NULL; + } else { + if(result) + *result = kmm_handle_from_module(m); + else + kmm_release_module(kmm_handle_from_module(m)); + } + } else { + kmm_hold_module(kmm_handle_from_module(m)); + kmq_post_message(KMSG_KMM, + KMSG_KMM_I_REG, + KMM_REG_INIT_MODULE, + (void*) m); + if(result) + *result = kmm_handle_from_module(m); + else + kmm_release_module(kmm_handle_from_module(m)); + } + + return rv; +} + +KHMEXP khm_int32 KHMAPI kmm_get_module_state(kmm_module m) +{ + if(!kmm_is_module(m)) + return KMM_MODULE_STATE_NONE; + else + return kmm_module_from_handle(m)->state; +} + +KHMEXP khm_int32 KHMAPI +kmm_get_module_info_i(kmm_module vm, kmm_module_info * info) { + kmm_module_i * m; + khm_int32 rv; + + EnterCriticalSection(&cs_kmm); + if (!kmm_is_module(vm) || !info) + rv = KHM_ERROR_INVALID_PARM; + else { + m = kmm_module_from_handle(vm); + + ZeroMemory(info, sizeof(*info)); + + info->reg.name = m->name; + info->reg.path = m->path; + info->reg.vendor = m->vendor; + + info->reg.n_plugins = m->plugin_count; + + info->state = m->state; + + info->h_module = vm; + kmm_hold_module(vm); + + rv = KHM_ERROR_SUCCESS; + } + LeaveCriticalSection(&cs_kmm); + + return rv; +} + +KHMEXP khm_int32 KHMAPI +kmm_release_module_info_i(kmm_module_info * info) { + if (info->h_module) + kmm_release_module(info->h_module); + + ZeroMemory(info, sizeof(*info)); + + return KHM_ERROR_SUCCESS; +} + + +KHMEXP khm_int32 KHMAPI kmm_unload_module(kmm_module module) +{ + if(!kmm_is_module(module)) + return KHM_ERROR_INVALID_PARM; + + kmm_hold_module(module); + kmq_post_message(KMSG_KMM, + KMSG_KMM_I_REG, + KMM_REG_EXIT_MODULE, + (void *) kmm_module_from_handle(module)); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kmm_load_default_modules(void) { + khm_handle csm = NULL; + khm_int32 rv; + wchar_t * ll = NULL; + wchar_t *str; + wchar_t buf[KMM_MAXCCH_NAME]; + khm_size s; + + rv = kmm_get_modules_config(0, &csm); + if(KHM_FAILED(rv)) + return rv; + + _begin_task(KHERR_CF_TRANSITIVE); + _report_mr0(KHERR_NONE, MSG_LOAD_DEFAULT); + _describe(); + + rv = khc_read_multi_string(csm, KMM_VALNAME_LOADLIST, NULL, &s); + if(rv != KHM_ERROR_TOO_LONG) + goto _exit; + + ll = malloc(s); + rv = khc_read_multi_string(csm, KMM_VALNAME_LOADLIST, ll, &s); + if(KHM_FAILED(rv)) + goto _exit; + + kmmint_add_to_module_queue(); + + str = ll; + while(str && *str) { + if(SUCCEEDED(StringCbCopy(buf, sizeof(buf), str))) { + kmm_load_module(buf, 0, NULL); + } + str = multi_string_next(str); + } + + kmmint_remove_from_module_queue(); + +_exit: + if(ll) + free(ll); + if(csm) + khc_close_space(csm); + + _end_task(); + + return rv; +} + +#ifdef _WIN32 +KHMEXP HMODULE KHMAPI kmm_get_hmodule(kmm_module m) +{ + if(!kmm_is_module(m)) + return NULL; + else + return kmm_module_from_handle(m)->h_module; +} +#endif diff --git a/src/windows/identity/kmm/kmm_plugin.c b/src/windows/identity/kmm/kmm_plugin.c new file mode 100644 index 000000000..f37eaa186 --- /dev/null +++ b/src/windows/identity/kmm/kmm_plugin.c @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include + +/* Called with no locks held to get a kmm_plugin_i structure + that matches the name. First we look in the hash table, and + if one isn't found, we create an empty one. +*/ + +kmm_plugin_i * +kmm_get_plugin_i(wchar_t * name) +{ + kmm_plugin_i * p; + size_t cb; + + if(FAILED(StringCbLength(name, KMM_MAXCB_NAME, &cb))) + return NULL; + cb += sizeof(wchar_t); + + EnterCriticalSection(&cs_kmm); + p = (kmm_plugin_i *) hash_lookup(hash_plugins, (void *) name); + + if(p == NULL) { + p = malloc(sizeof(kmm_plugin_i)); + ZeroMemory(p, sizeof(kmm_plugin_i)); + p->magic = KMM_PLUGIN_MAGIC; + p->p.name = malloc(cb); + StringCbCopy(p->p.name, cb, name); + p->state = KMM_PLUGIN_STATE_NONE; + + hash_add(hash_plugins, (void *) p->p.name, (void *) p); + kmm_list_plugin(p); + } + LeaveCriticalSection(&cs_kmm); + + return p; +} + +kmm_plugin_i * +kmm_find_plugin_i(wchar_t * name) +{ + kmm_plugin_i * p; + size_t cb; + + if(FAILED(StringCbLength(name, KMM_MAXCB_NAME, &cb))) + return NULL; + + EnterCriticalSection(&cs_kmm); + p = (kmm_plugin_i *) hash_lookup(hash_plugins, (void *) name); + LeaveCriticalSection(&cs_kmm); + + return p; +} + +/* the plugin must be delisted before calling this */ +void +kmm_list_plugin(kmm_plugin_i * p) +{ + EnterCriticalSection(&cs_kmm); + if((p->flags & KMM_PLUGIN_FLAG_IN_MODLIST) || + (p->flags & KMM_PLUGIN_FLAG_IN_LIST)) + { + RaiseException(2, EXCEPTION_NONCONTINUABLE, 0, NULL); + } + p->flags |= KMM_PLUGIN_FLAG_IN_LIST; + LPUSH(&kmm_listed_plugins, p); + LeaveCriticalSection(&cs_kmm); +} + +void +kmm_delist_plugin(kmm_plugin_i * p) +{ + EnterCriticalSection(&cs_kmm); + if(p->flags & KMM_PLUGIN_FLAG_IN_LIST) { + p->flags &= ~KMM_PLUGIN_FLAG_IN_LIST; + LDELETE(&kmm_listed_plugins, p); + } + if(p->flags & KMM_PLUGIN_FLAG_IN_MODLIST) { + p->flags &= ~KMM_PLUGIN_FLAG_IN_MODLIST; + LDELETE(&(p->module->plugins), p); + } + LeaveCriticalSection(&cs_kmm); +} + +KHMEXP khm_int32 KHMAPI +kmm_hold_plugin(kmm_plugin p) +{ + kmm_plugin_i * pi; + + if(!kmm_is_plugin(p)) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_kmm); + pi = kmm_plugin_from_handle(p); + pi->refcount++; + LeaveCriticalSection(&cs_kmm); + + return KHM_ERROR_SUCCESS; +} + +/* called with cs_kmm held */ +void +kmm_free_plugin(kmm_plugin_i * pi) +{ + int i; + pi->magic = 0; + + hash_del(hash_plugins, (void *) pi->p.name); + + kmm_delist_plugin(pi); + + for(i=0; in_dependants; i++) { + kmm_release_plugin(kmm_handle_from_plugin(pi->dependants[i])); + pi->dependants[i] = NULL; + } + + if(pi->module) { + kmm_release_module(kmm_handle_from_module(pi->module)); + } + + pi->module = NULL; + pi->p.module = NULL; + + if(pi->p.name) + free(pi->p.name); + pi->p.name = NULL; + + if(pi->p.description) + free(pi->p.description); + pi->p.description = NULL; + + if(pi->p.dependencies) + free(pi->p.dependencies); + pi->p.dependencies = NULL; + + free(pi); +} + +KHMEXP khm_int32 KHMAPI +kmm_get_plugin_info_i(kmm_plugin p, kmm_plugin_info * info) { + khm_int32 rv = KHM_ERROR_SUCCESS; + kmm_plugin_i * pi; + khm_handle csp_plugin; + + if (!info) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_kmm); + if (!kmm_is_plugin(p)) { + rv = KHM_ERROR_INVALID_PARM; + goto _cleanup; + } + + pi = kmm_plugin_from_handle(p); + + ZeroMemory(info, sizeof(*info)); + + info->reg = pi->p; + info->reg.msg_proc = NULL; + + if (KHM_FAILED(kmm_get_plugin_config(pi->p.name, KHM_PERM_READ, + &csp_plugin))) { + info->failure_count = 0; + *((khm_int64 *)&info->failure_time) = 0; + info->failure_reason = 0; + } else { + if (KHM_FAILED(khc_read_int32(csp_plugin, L"FailureCount", + &info->failure_count))) + info->failure_count = 0; + if (KHM_FAILED(khc_read_int64(csp_plugin, L"FailureTime", + (khm_int64 *) &info->failure_time))) + *((khm_int64 *) &info->failure_time) = 0; + if (KHM_FAILED(khc_read_int32(csp_plugin, L"FailureReason", + &info->failure_reason))) + info->failure_reason = 0; + + khc_close_space(csp_plugin); + } + + info->state = pi->state; + + info->h_plugin = p; + kmm_hold_plugin(p); + + _cleanup: + LeaveCriticalSection(&cs_kmm); + + return rv; +} + +KHMEXP khm_int32 KHMAPI +kmm_release_plugin_info_i(kmm_plugin_info * info) { + khm_int32 rv; + + if (!info || !info->h_plugin) + return KHM_ERROR_INVALID_PARM; + + rv = kmm_release_plugin(info->h_plugin); + + ZeroMemory(info, sizeof(info)); + + return rv; +} + +KHMEXP khm_int32 KHMAPI +kmm_get_next_plugin(kmm_plugin p, kmm_plugin * p_next) { + khm_int32 rv = KHM_ERROR_SUCCESS; + kmm_plugin_i * pi; + kmm_plugin_i * pi_next = NULL; + kmm_module_i * m; + + EnterCriticalSection(&cs_kmm); + if (p == NULL) { + if (kmm_listed_plugins) + pi_next = kmm_listed_plugins; + else { + for (m = kmm_all_modules; m; m = LNEXT(m)) { + if (m->plugins) { + pi_next = m->plugins; + break; + } + } + } + } else if (kmm_is_plugin(p)) { + pi = kmm_plugin_from_handle(p); + pi_next = LNEXT(pi); + + if (!pi_next) { + /* we have either exhausted the listed plugins or we are + at the end of the module's plugin list */ + if (pi->module) { + m = LNEXT(pi->module); + } else { + m = kmm_all_modules; + } + + for(; m; m = LNEXT(m)) { + if (m->plugins) { + pi_next = m->plugins; + break; + } + } + } + } + + if (pi_next) { + *p_next = kmm_handle_from_plugin(pi_next); + kmm_hold_plugin(*p_next); + } else { + *p_next = NULL; + rv = KHM_ERROR_NOT_FOUND; + } + + LeaveCriticalSection(&cs_kmm); + return rv; +} + +KHMEXP khm_int32 KHMAPI +kmm_release_plugin(kmm_plugin p) +{ + kmm_plugin_i * pi; + + if(!kmm_is_plugin(p)) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_kmm); + pi = kmm_plugin_from_handle(p); + pi->refcount--; + if(pi->refcount == 0) { + kmm_free_plugin(pi); + } + LeaveCriticalSection(&cs_kmm); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +kmm_provide_plugin(kmm_module module, kmm_plugin_reg * plugin) +{ + kmm_module_i * m; + kmm_plugin_i * p; + size_t cb_name = 0; + size_t cb_desc = 0; + size_t cb_dep = 0; + + m = kmm_module_from_handle(module); + + /* can only called when handing init_module() */ + if(m->state != KMM_MODULE_STATE_INIT) + return KHM_ERROR_INVALID_OPERATION; + + if(!plugin || + FAILED(StringCbLength(plugin->name, KMM_MAXCB_NAME - sizeof(wchar_t), &cb_name)) || + (plugin->description && + FAILED(StringCbLength(plugin->description, KMM_MAXCB_DESC - sizeof(wchar_t), &cb_desc))) || + (plugin->dependencies && + KHM_FAILED(multi_string_length_cb(plugin->dependencies, KMM_MAXCB_DEPS, &cb_dep))) + ) + { + return KHM_ERROR_INVALID_PARM; + } + + cb_name += sizeof(wchar_t); + cb_desc += sizeof(wchar_t); + + p = kmm_get_plugin_i(plugin->name); + + /* released below or in kmm_init_module() */ + kmm_hold_plugin(kmm_handle_from_plugin(p)); + + if(p->state != KMM_PLUGIN_STATE_NONE && + p->state != KMM_PLUGIN_STATE_PLACEHOLDER) + { + kmm_release_plugin(kmm_handle_from_plugin(p)); + return KHM_ERROR_DUPLICATE; + } + + /* released when the plugin quits */ + kmm_hold_module(module); + + p->module = m; + p->p.flags = plugin->flags; + p->p.msg_proc = plugin->msg_proc; + p->p.type = plugin->type; + + if(plugin->description) { + p->p.description = malloc(cb_desc); + StringCbCopy(p->p.description, cb_desc, plugin->description); + } else + p->p.description = NULL; + + if(plugin->dependencies) { + p->p.dependencies = malloc(cb_dep); + multi_string_copy_cb(p->p.dependencies, cb_dep, plugin->dependencies); + } else + p->p.dependencies = NULL; + + p->p.module = p->module->name; + + p->p.icon = plugin->icon; + + p->state = KMM_PLUGIN_STATE_REG; + + kmm_delist_plugin(p); + EnterCriticalSection(&cs_kmm); + LPUSH(&(m->plugins), p); + p->flags |= KMM_PLUGIN_FLAG_IN_MODLIST; + LeaveCriticalSection(&cs_kmm); + + /* leave the plugin held because it is in the module's plugin list */ + return KHM_ERROR_SUCCESS; +} + diff --git a/src/windows/identity/kmm/kmm_reg.c b/src/windows/identity/kmm/kmm_reg.c new file mode 100644 index 000000000..ea13fc19b --- /dev/null +++ b/src/windows/identity/kmm/kmm_reg.c @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include + +KHMEXP khm_int32 KHMAPI +kmm_get_module_info(wchar_t * module_name, khm_int32 flags, + kmm_module_info * buffer, khm_size * cb_buffer) +{ + /*TODO:Implement this */ + return KHM_ERROR_NOT_IMPLEMENTED; +} + +KHMEXP khm_int32 KHMAPI +kmm_get_plugin_info(wchar_t * plugin_name, + kmm_plugin_info * buffer, khm_size * cb_buffer) +{ + /*TODO:Implement this */ + return KHM_ERROR_NOT_IMPLEMENTED; +} + +KHMEXP khm_int32 KHMAPI +kmm_get_plugins_config(khm_int32 flags, khm_handle * result) { + khm_handle csp_root; + khm_handle csp_plugins; + khm_int32 rv; + + rv = khc_open_space(KHM_INVALID_HANDLE, KMM_CSNAME_ROOT, flags, &csp_root); + + if(KHM_FAILED(rv)) + return rv; + + rv = khc_open_space(csp_root, KMM_CSNAME_PLUGINS, flags, &csp_plugins); + khc_close_space(csp_root); + + if(KHM_SUCCEEDED(rv)) + *result = csp_plugins; + else + *result = NULL; + + return rv; +} + + +KHMEXP khm_int32 KHMAPI +kmm_get_modules_config(khm_int32 flags, khm_handle * result) { + khm_handle croot; + khm_handle kmm_all_modules; + khm_int32 rv; + + rv = khc_open_space(NULL, KMM_CSNAME_ROOT, flags, &croot); + + if(KHM_FAILED(rv)) + return rv; + + rv = khc_open_space(croot, KMM_CSNAME_MODULES, flags, &kmm_all_modules); + khc_close_space(croot); + + if(KHM_SUCCEEDED(rv)) + *result = kmm_all_modules; + else + *result = NULL; + + return rv; +} + + +KHMEXP khm_int32 KHMAPI +kmm_get_plugin_config(wchar_t * plugin, khm_int32 flags, khm_handle * result) +{ + khm_handle csplugins; + khm_handle csplugin; + khm_int32 rv; + + if(!plugin || wcschr(plugin, L'/') || wcschr(plugin, L'\\')) + return KHM_ERROR_INVALID_PARM; + + if(KHM_FAILED(kmm_get_plugins_config(flags, &csplugins))) + return KHM_ERROR_UNKNOWN; + + rv = khc_open_space(csplugins, plugin, flags, &csplugin); + *result = csplugin; + + khc_close_space(csplugins); + + return rv; +} + + +KHMEXP khm_int32 KHMAPI +kmm_get_module_config(wchar_t * module, khm_int32 flags, khm_handle * result) +{ + khm_handle csmodules; + khm_handle csmodule; + khm_int32 rv; + + if(!module || wcschr(module, L'/') || wcschr(module, L'\\')) + return KHM_ERROR_INVALID_PARM; + + if(KHM_FAILED(kmm_get_modules_config(flags, &csmodules))) + return KHM_ERROR_UNKNOWN; + + rv = khc_open_space(csmodules, module, flags, &csmodule); + *result = csmodule; + + khc_close_space(csmodules); + + return rv; +} + +KHMEXP khm_int32 KHMAPI +kmm_register_plugin(kmm_plugin_reg * plugin, khm_int32 config_flags) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + khm_handle csp_plugin = NULL; + khm_handle csp_module = NULL; + size_t cch; + + /* avoid accidently creating the module key if it doesn't exist */ + config_flags &= ~KHM_FLAG_CREATE; + + if((plugin == NULL) || + (plugin->dependencies && + KHM_FAILED(multi_string_length_cch(plugin->dependencies, KMM_MAXCCH_DEPS, &cch))) || + FAILED(StringCchLength(plugin->module, KMM_MAXCCH_NAME - 1, &cch)) || + (plugin->description && + FAILED(StringCchLength(plugin->description, KMM_MAXCCH_DESC - 1, &cch))) || + FAILED(StringCchLength(plugin->name, KMM_MAXCCH_NAME - 1, &cch))) + { + return KHM_ERROR_INVALID_PARM; + } + + /* note that we are retaining the length of the plugin name in + chars in cch */ + cch ++; + +#define CKRV if(KHM_FAILED(rv)) goto _exit + + rv = kmm_get_plugin_config(plugin->name, + config_flags | KHM_FLAG_CREATE, &csp_plugin); + CKRV; + + /* should fail if the module key doesn't exist */ + rv = kmm_get_module_config(plugin->module, config_flags, &csp_module); + CKRV; + + /*TODO: Make sure that the module registration is in the same + config store as the one in which the plugin is going to be + registered */ + + rv = khc_write_string(csp_plugin, L"Module", plugin->module); + CKRV; + if(plugin->description) { + rv = khc_write_string(csp_plugin, L"Description", plugin->description); + CKRV; + } + if(plugin->dependencies) { + rv = khc_write_multi_string(csp_plugin, L"Dependencies", plugin->dependencies); + CKRV; + } + rv = khc_write_int32(csp_plugin, L"Type", plugin->type); + CKRV; + rv = khc_write_int32(csp_plugin, L"Flags", plugin->flags); + CKRV; + + { + khm_size cb = 0; + wchar_t * pl = NULL; + size_t scb = 0; + + rv = khc_read_multi_string(csp_module, L"PluginList", NULL, &cb); + if(rv != KHM_ERROR_TOO_LONG) + goto _exit; + + cb += cch * sizeof(wchar_t); + scb = cb; + + pl = malloc(cb); + + rv = khc_read_multi_string(csp_module, L"PluginList", NULL, &cb); + if(KHM_FAILED(rv)) { + if(pl) + free(pl); + goto _exit; + } + + if(!multi_string_find(pl, plugin->name, 0)) { + multi_string_append(pl, &scb, plugin->name); + rv = khc_write_multi_string(csp_module, L"PluginList", pl); + } + + free(pl); + CKRV; + } + +#undef CKRV + +_exit: + if(csp_plugin) + khc_close_space(csp_plugin); + if(csp_module) + khc_close_space(csp_module); + + return rv; +} + +KHMEXP khm_int32 KHMAPI +kmm_register_module(kmm_module_reg * module, khm_int32 config_flags) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + khm_handle csp_module = NULL; + size_t cch; + int i; + + if((module == NULL) || + FAILED(StringCchLength(module->name, KMM_MAXCCH_NAME - 1, &cch)) || + (module->description && + FAILED(StringCchLength(module->description, KMM_MAXCCH_DESC - 1, &cch))) || + FAILED(StringCchLength(module->path, MAX_PATH, &cch)) || + (module->n_plugins > 0 && module->plugin_reg_info == NULL)) + { + return KHM_ERROR_INVALID_PARM; + } + +#define CKRV if(KHM_FAILED(rv)) goto _exit + + rv = kmm_get_module_config(module->name, config_flags | KHM_FLAG_CREATE, &csp_module); + CKRV; + + if(module->description) { + rv = khc_write_string(csp_module, L"Description", module->description); + CKRV; + } + rv = khc_write_string(csp_module, L"ImagePath", module->path); + CKRV; + + rv = khc_write_int32(csp_module, L"Flags", 0); + CKRV; + + /* FileVersion and ProductVersion will be set when the module + is loaded for the first time */ + + for(i=0; in_plugins; i++) { + rv = kmm_register_plugin(module->plugin_reg_info + i, config_flags); + CKRV; + } + +#undef CKRV +_exit: + if(csp_module) + khc_close_space(csp_module); + + return rv; +} + +KHMEXP khm_int32 KHMAPI +kmm_unregister_plugin(wchar_t * plugin, khm_int32 config_flags) +{ + /*TODO: implement this */ + return KHM_ERROR_NOT_IMPLEMENTED; +} + +KHMEXP khm_int32 KHMAPI +kmm_unregister_module(wchar_t * module, khm_int32 config_flags) +{ + /*TODO: implement this */ + return KHM_ERROR_NOT_IMPLEMENTED; +} diff --git a/src/windows/identity/kmm/kmm_registrar.c b/src/windows/identity/kmm/kmm_registrar.c new file mode 100644 index 000000000..3c690f36b --- /dev/null +++ b/src/windows/identity/kmm/kmm_registrar.c @@ -0,0 +1,836 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include + +static LONG pending_modules = 0; +static LONG pending_plugins = 0; +static LONG startup_signal = 0; +static BOOL load_done = FALSE; + +void +kmmint_check_completion(void) { + if (pending_modules == 0 && + pending_plugins == 0 && + InterlockedIncrement(&startup_signal) == 1) { + + load_done = TRUE; + kmq_post_message(KMSG_KMM, KMSG_KMM_I_DONE, 0, 0); + } +} + +void +kmmint_add_to_module_queue(void) { + InterlockedIncrement(&pending_modules); +} + +void +kmmint_remove_from_module_queue(void) { + + InterlockedDecrement(&pending_modules); + + kmmint_check_completion(); +} + +void +kmmint_add_to_plugin_queue(void) { + InterlockedIncrement(&pending_plugins); +} + +void +kmmint_remove_from_plugin_queue(void) { + InterlockedDecrement(&pending_plugins); + + kmmint_check_completion(); +} + +KHMEXP khm_boolean KHMAPI +kmm_load_pending(void) { + return !load_done; +} + +/*! \internal + \brief Message handler for the registrar thread. */ +khm_boolean KHMAPI kmm_reg_cb( + khm_int32 msg_type, + khm_int32 msg_sub_type, + khm_ui_4 uparam, + void *vparam) +{ + /* we should only be getting anyway */ + if(msg_type != KMSG_KMM || msg_sub_type != KMSG_KMM_I_REG) + return FALSE; + + switch(uparam) { + case KMM_REG_INIT_MODULE: + kmm_init_module((kmm_module_i *) vparam); + kmm_release_module(kmm_handle_from_module((kmm_module_i *) vparam)); + break; + + case KMM_REG_EXIT_MODULE: + kmm_exit_module((kmm_module_i *) vparam); + kmm_release_module(kmm_handle_from_module((kmm_module_i *) vparam)); + break; + + case KMM_REG_INIT_PLUGIN: + kmm_init_plugin((kmm_plugin_i *) vparam); + kmm_release_plugin(kmm_handle_from_plugin((kmm_plugin_i *) vparam)); + break; + + case KMM_REG_EXIT_PLUGIN: + kmm_exit_plugin((kmm_plugin_i *) vparam); + kmm_release_plugin(kmm_handle_from_plugin((kmm_plugin_i *) vparam)); + break; + } + return TRUE; +} + +/*! \internal + \brief The registrar thread. + + The only thing this function does is to dispatch messages to the + callback routine ( kmm_reg_cb() ) */ +DWORD WINAPI kmm_registrar( + LPVOID lpParameter +) +{ + tid_registrar = GetCurrentThreadId(); + + kmq_subscribe(KMSG_KMM, kmm_reg_cb); + kmq_subscribe(KMSG_SYSTEM, kmm_reg_cb); + + SetEvent(evt_startup); + + while(KHM_SUCCEEDED(kmq_dispatch(INFINITE))); + + ExitThread(0); + /* not reached */ + return 0; +} + +/*! \internal + \brief Manages a plugin message thread. + + Each plugin gets its own plugin thread which is used to dispatch + messages to the plugin. This acts as the thread function for the + plugin thread.*/ +DWORD WINAPI kmm_plugin_broker(LPVOID lpParameter) +{ + DWORD rv = 0; + kmm_plugin_i * p = (kmm_plugin_i *) lpParameter; + + TlsSetValue(tls_kmm, (LPVOID) p); + + kmm_hold_plugin(kmm_handle_from_plugin(p)); + + p->tid_thread = GetCurrentThreadId(); + + rv = (p->p.msg_proc(KMSG_SYSTEM, KMSG_SYSTEM_INIT, 0, (void *) &(p->p))); + + /* if it fails to initialize, we exit the plugin */ + if(KHM_FAILED(rv)) { + kmmint_remove_from_plugin_queue(); + rv = 1; + goto _exit; + } + + /* subscribe to default message classes by plugin type */ + if(p->p.type & KHM_PITYPE_CRED) { + kmq_subscribe(KMSG_SYSTEM, p->p.msg_proc); + kmq_subscribe(KMSG_KCDB, p->p.msg_proc); + kmq_subscribe(KMSG_CRED, p->p.msg_proc); + } + + if(p->p.flags & KHM_PIFLAG_IDENTITY_PROVIDER) { + khm_handle h = NULL; + + kmq_create_subscription(p->p.msg_proc, &h); + kcdb_identity_set_provider(h); + /* kcdb deletes the subscription when it's done with it */ + } + + if(p->p.type == KHM_PITYPE_CONFIG) { + /*TODO: subscribe to configuration provider messages here */ + } + + p->state = KMM_PLUGIN_STATE_RUNNING; + + /* if there were any plugins that were waiting for this one to + start, we should start them too */ + EnterCriticalSection(&cs_kmm); + do { + kmm_plugin_i * pd; + int i; + + for(i=0; i < p->n_dependants; i++) { + pd = p->dependants[i]; + + pd->n_unresolved--; + + if(pd->n_unresolved == 0) { + kmm_hold_plugin(kmm_handle_from_plugin(pd)); + kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, KMM_REG_INIT_PLUGIN, (void *) pd); + } + } + } while(FALSE); + LeaveCriticalSection(&cs_kmm); + + kmmint_remove_from_plugin_queue(); + + /* main message loop */ + while(KHM_SUCCEEDED(kmq_dispatch(INFINITE))); + + /* unsubscribe from default message classes by plugin type */ + if(p->p.type & KHM_PITYPE_CRED) { + kmq_unsubscribe(KMSG_SYSTEM, p->p.msg_proc); + kmq_unsubscribe(KMSG_KCDB, p->p.msg_proc); + kmq_unsubscribe(KMSG_CRED, p->p.msg_proc); + } + + if(p->p.flags & KHM_PIFLAG_IDENTITY_PROVIDER) { + kcdb_identity_set_provider(NULL); + } + + if(p->p.type == KHM_PITYPE_CONFIG) { + /*TODO: unsubscribe from configuration provider messages here */ + } + + p->p.msg_proc(KMSG_SYSTEM, KMSG_SYSTEM_EXIT, 0, (void *) &(p->p)); + +_exit: + p->state = KMM_PLUGIN_STATE_EXITED; + + /* the following call will automatically release the plugin */ + kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, KMM_REG_EXIT_PLUGIN, (void *) p); + + TlsSetValue(tls_kmm, (LPVOID) 0); + + ExitThread(rv); + + /* not reached */ + return rv; +} + +/*! \internal + \brief Initialize a plugin + + \note If kmm_init_plugin() is called on a plugin, then kmm_exit_plugin() + \b must be called for the plugin. + + \note Should only be called from the context of the registrar thread */ +void kmm_init_plugin(kmm_plugin_i * p) { + DWORD dummy; + khm_handle csp_plugin = NULL; + khm_handle csp_plugins = NULL; + khm_int32 t; + + /* the following will be undone in kmm_exit_plugin() */ + kmm_hold_plugin(kmm_handle_from_plugin(p)); + + EnterCriticalSection(&cs_kmm); + if(p->state != KMM_PLUGIN_STATE_REG && + p->state != KMM_PLUGIN_STATE_HOLD) + { + LeaveCriticalSection(&cs_kmm); + goto _exit; + } + + _begin_task(0); + _report_mr1(KHERR_NONE, MSG_IP_TASK_DESC, _cstr(p->p.name)); + _describe(); + + if(p->state == KMM_PLUGIN_STATE_HOLD) { + /* if this plugin was held, then we already had a hold + from the initial attempt to start the plugin. Undo + the hold we did a few lines earlier. */ + kmm_release_plugin(kmm_handle_from_plugin(p)); + /* same for the plugin count for the module. */ + p->module->plugin_count--; + } + + p->state = KMM_PLUGIN_STATE_PREINIT; + LeaveCriticalSection(&cs_kmm); + + if(KHM_FAILED(kmm_get_plugins_config(0, &csp_plugins))) { + _report_mr0(KHERR_ERROR, MSG_IP_GET_CONFIG); + + p->state = KMM_PLUGIN_STATE_FAIL_UNKNOWN; + goto _exit; + } + + if(KHM_FAILED(kmm_get_plugin_config(p->p.name, 0, &csp_plugin)) || + KHM_FAILED(khc_read_int32(csp_plugin, L"Flags", &t))) + { + if(KHM_FAILED(kmm_register_plugin(&(p->p), 0))) { + _report_mr0(KHERR_ERROR, MSG_IP_NOT_REGISTERED); + + p->state = KMM_PLUGIN_STATE_FAIL_NOT_REGISTERED; + goto _exit; + } + + if(KHM_FAILED(kmm_get_plugin_config(p->p.name, 0, &csp_plugin))) { + _report_mr0(KHERR_ERROR, MSG_IP_NOT_REGISTERED); + + p->state = KMM_PLUGIN_STATE_FAIL_NOT_REGISTERED; + goto _exit; + } + + if(KHM_FAILED(khc_read_int32(csp_plugin, L"Flags", &t))) { + _report_mr0(KHERR_ERROR, MSG_IP_NOT_REGISTERED); + + p->state = KMM_PLUGIN_STATE_FAIL_NOT_REGISTERED; + goto _exit; + } + } + + if(t & KMM_PLUGIN_FLAG_DISABLED) { + _report_mr0(KHERR_ERROR, MSG_IP_DISABLED); + + p->state = KMM_PLUGIN_STATE_FAIL_DISABLED; + goto _exit; + } + +#if 0 + /*TODO: check the failure count and act accordingly */ + if(KHM_SUCCEEDED(khc_read_int32(csp_plugin, L"FailureCount", &t)) && (t > 0)) { + } +#endif + + EnterCriticalSection(&cs_kmm); + + p->n_depends = 0; + p->n_unresolved = 0; + + do { + wchar_t * deps = NULL; + wchar_t * d; + khm_size sz = 0; + + if(khc_read_multi_string(csp_plugin, L"Dependencies", NULL, &sz) != KHM_ERROR_TOO_LONG) + break; + + deps = malloc(sz); + if(KHM_FAILED(khc_read_multi_string(csp_plugin, L"Dependencies", deps, &sz))) { + if(deps) + free(deps); + break; + } + + for(d = deps; d && *d; d = multi_string_next(d)) { + kmm_plugin_i * pd; + int i; + + pd = kmm_get_plugin_i(d); + + if(pd->state == KMM_PLUGIN_STATE_NONE) { + /* the dependant was not previously known */ + pd->state = KMM_PLUGIN_STATE_PLACEHOLDER; + } + + for(i=0; in_dependants; i++) { + if(pd->dependants[i] == p) + break; + } + + if(i >= pd->n_dependants) { + if( pd->n_dependants >= KMM_MAX_DEPENDANTS ) { + /*TODO: handle this gracefully */ + RaiseException(1, EXCEPTION_NONCONTINUABLE, 0, NULL); + } + + /* released in kmm_free_plugin() */ + kmm_hold_plugin(kmm_handle_from_plugin(p)); + pd->dependants[pd->n_dependants] = p; + pd->n_dependants++; + } + + p->n_depends++; + + if(pd->state != KMM_PLUGIN_STATE_RUNNING) { + p->n_unresolved++; + } + } + + if(p->n_unresolved > 0) { + p->state = KMM_PLUGIN_STATE_HOLD; + } + + free(deps); + + } while(FALSE); + LeaveCriticalSection(&cs_kmm); + + EnterCriticalSection(&cs_kmm); + p->module->plugin_count++; + kmm_delist_plugin(p); + kmm_list_plugin(p); + LeaveCriticalSection(&cs_kmm); + + if(p->state == KMM_PLUGIN_STATE_HOLD) { + _report_mr1(KHERR_INFO, MSG_IP_HOLD, _dupstr(p->p.name)); + + goto _exit_post; + } + + kmmint_add_to_plugin_queue(); + + p->ht_thread = CreateThread( + NULL, + 0, + kmm_plugin_broker, + (LPVOID) p, + CREATE_SUSPENDED, + &dummy); + + p->state = KMM_PLUGIN_STATE_INIT; + + ResumeThread(p->ht_thread); + +_exit_post: + if(csp_plugin != NULL) + khc_close_space(csp_plugin); + + if(csp_plugins != NULL) + khc_close_space(csp_plugins); + + _report_mr2(KHERR_INFO, MSG_IP_STATE, + _dupstr(p->p.name), _int32(p->state)); + + _end_task(); + + return; + + /* jump here if an error condition happens before the plugin + broker thread starts and the plugin should be unloaded */ + +_exit: + if(csp_plugin != NULL) + khc_close_space(csp_plugin); + if(csp_plugins != NULL) + khc_close_space(csp_plugins); + + _report_mr2(KHERR_WARNING, MSG_IP_EXITING, + _dupstr(p->p.name), _int32(p->state)); + _end_task(); + + kmm_hold_plugin(kmm_handle_from_plugin(p)); + + kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, KMM_REG_EXIT_PLUGIN, (void *) p); +} + +/*! \internal + \brief Uninitialize a plugin + + In addition to terminating the thread, and removing p from the + linked list and hashtable, it also frees up p. + + \note Should only be called from the context of the registrar thread. */ +void kmm_exit_plugin(kmm_plugin_i * p) { + int np; + + if(p->state == KMM_PLUGIN_STATE_RUNNING || + p->state == KMM_PLUGIN_STATE_INIT) + { + kmq_post_thread_quit_message(p->tid_thread, 0, NULL); + /* when we post the quit message to the plugin thread, the plugin + broker terminates the plugin and posts a EXIT_PLUGIN message, + which calls this function again. We just exit here because + the EXIT_PLUGIN message will end up calling us again momentarily */ + return; + } + + if(p->ht_thread) { + /* wait for the thread to terminate */ + WaitForSingleObject(p->ht_thread, INFINITE); + p->ht_thread = NULL; + } + + EnterCriticalSection(&cs_kmm); + + /* undo reference count done in kmm_init_plugin() */ + if(p->state == KMM_PLUGIN_STATE_EXITED || + p->state == KMM_PLUGIN_STATE_HOLD) + { + np = --(p->module->plugin_count); + } else { + /* the plugin was never active. We can't base a module unload + decision on np */ + np = TRUE; + } + LeaveCriticalSection(&cs_kmm); + + if(!np) { + /* if this is the last plugin to exit, then notify the + registrar that the module should be removed as well */ + kmm_hold_module(kmm_handle_from_module(p->module)); + kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, KMM_REG_EXIT_MODULE, (void *) p->module); + } + + /* release the hold obtained in kmm_init_plugin() */ + kmm_release_plugin(kmm_handle_from_plugin(p)); +} + +/*! \internal + \brief Initialize a module + + \a m is not in the linked list yet. + + \note Should only be called from the context of the registrar thread. */ +void kmm_init_module(kmm_module_i * m) { + HMODULE hm; + init_module_t p_init_module; + kmm_plugin_i * pi; + khm_int32 rv; + khm_handle csp_mod = NULL; + khm_handle csp_mods = NULL; + khm_size sz; + khm_int32 i; + + /* error condition handling */ + BOOL exit_module = FALSE; + BOOL release_module = TRUE; + BOOL record_failure = FALSE; + + /* failure handling */ + khm_int32 max_fail_count = 0; + khm_int64 fail_reset_time = 0; + + _begin_task(0); + _report_mr1(KHERR_NONE, MSG_INIT_MODULE, _cstr(m->name)); + _describe(); + + kmm_hold_module(kmm_handle_from_module(m)); + + if(KHM_FAILED(kmm_get_modules_config(0, &csp_mods))) { + _report_mr0(KHERR_ERROR, MSG_IM_GET_CONFIG); + _location(L"kmm_get_modules_config()"); + + m->state = KMM_MODULE_STATE_FAIL_UNKNOWN; + goto _exit; + } + + khc_read_int32(csp_mods, L"ModuleMaxFailureCount", &max_fail_count); + khc_read_int64(csp_mods, L"ModuleFailureCountResetTime", &fail_reset_time); + + /* If the module is not in the pre-init state, we can't + initialize it. */ + if(m->state != KMM_MODULE_STATE_PREINIT) { + _report_mr1(KHERR_WARNING, MSG_IM_NOT_PREINIT, _int32(m->state)); + goto _exit; + } + + if(KHM_FAILED(kmm_get_module_config(m->name, 0, &csp_mod))) { + _report_mr0(KHERR_ERROR, MSG_IM_NOT_REGISTERED); + + m->state = KMM_MODULE_STATE_FAIL_NOT_REGISTERED; + goto _exit; + } + + if(KHM_SUCCEEDED(khc_read_int32(csp_mod, L"Flags", &i))) { + if(i & KMM_MODULE_FLAG_DISABLED) { + _report_mr0(KHERR_ERROR, MSG_IM_DISABLED); + + m->state = KMM_MODULE_STATE_FAIL_DISABLED; + goto _exit; + } + } + + if(KHM_SUCCEEDED(khc_read_int32(csp_mod, L"FailureCount", &i))) { + khm_int64 tm; + khm_int64 ct; + + /* reset the failure count if the failure count reset time + period has elapsed */ + tm = 0; + khc_read_int64(csp_mod, L"FailureTime", &tm); + GetSystemTimeAsFileTime((LPFILETIME) &ct); + ct -= tm; + + if(tm > 0 && + FtIntervalToSeconds((LPFILETIME) &ct) > fail_reset_time) { + + i = 0; + khc_write_int32(csp_mod, L"FailureCount", 0); + khc_write_int64(csp_mod, L"FailureTime", 0); + + } + + if(i > max_fail_count) { + /* failed too many times */ + _report_mr0(KHERR_ERROR, MSG_IM_MAX_FAIL); + + m->state = KMM_MODULE_STATE_FAIL_MAX_FAILURE; + goto _exit; + } + } + + if(khc_read_string(csp_mod, L"ImagePath", NULL, &sz) == KHM_ERROR_TOO_LONG) { + if(m->path) + free(m->path); + m->path = malloc(sz); + khc_read_string(csp_mod, L"ImagePath", m->path, &sz); + } else { + _report_mr0(KHERR_ERROR, MSG_IM_NOT_REGISTERED); + + m->state = KMM_MODULE_STATE_FAIL_NOT_REGISTERED; + goto _exit; + } + + if (khc_read_string(csp_mod, L"Vendor", NULL, &sz) == KHM_ERROR_TOO_LONG) { + if (m->vendor) + free(m->vendor); + m->vendor = malloc(sz); + khc_read_string(csp_mod, L"Vendor", m->vendor, &sz); + } + + /* check again */ + if(m->state != KMM_MODULE_STATE_PREINIT) { + _report_mr0(KHERR_ERROR, MSG_IM_NOT_PREINIT); + + goto _exit; + } + + hm = LoadLibrary(m->path); + if(!hm) { + m->h_module = NULL; + m->state = KMM_MODULE_STATE_FAIL_NOT_FOUND; + record_failure = TRUE; + + _report_mr1(KHERR_ERROR, MSG_IM_NOT_FOUND, _dupstr(m->path)); + + goto _exit; + } + + /* from this point on, we need to discard the module through + exit_module */ + release_module = FALSE; + exit_module = TRUE; + record_failure = TRUE; + + m->flags |= KMM_MODULE_FLAG_LOADED; + m->h_module = hm; + + /*TODO: check signatures */ + + p_init_module = (init_module_t) GetProcAddress(hm, EXP_INIT_MODULE); + + if(!p_init_module) { + _report_mr1(KHERR_ERROR, MSG_IM_NO_ENTRY, _cstr(EXP_INIT_MODULE)); + + m->state = KMM_MODULE_STATE_FAIL_INVALID; + goto _exit; + } + + m->state = KMM_MODULE_STATE_INIT; + + + /* call init_module() */ + rv = (*p_init_module)(kmm_handle_from_module(m)); + + m->flags |= KMM_MODULE_FLAG_INITP; + + if(KHM_FAILED(rv)) { + _report_mr1(KHERR_ERROR, MSG_IM_INIT_FAIL, _int32(rv)); + + m->state = KMM_MODULE_STATE_FAIL_LOAD; + goto _exit; + } + + if(!m->plugins) { + _report_mr0(KHERR_ERROR, MSG_IM_NO_PLUGINS); + + m->state = KMM_MODULE_STATE_FAIL_NO_PLUGINS; + record_failure = FALSE; + goto _exit; + } + + m->state = KMM_MODULE_STATE_INITPLUG; + + do { + LPOP(&(m->plugins), &pi); + if(pi) { + pi->flags &= ~KMM_PLUGIN_FLAG_IN_MODLIST; + kmm_init_plugin(pi); + + /* release the hold obtained in kmm_provide_plugin() */ + kmm_release_plugin(kmm_handle_from_plugin(pi)); + } + } while(pi); + + if(!m->plugin_count) { + _report_mr0(KHERR_ERROR, MSG_IM_NO_PLUGINS); + + m->state = KMM_MODULE_STATE_FAIL_NO_PLUGINS; + record_failure = FALSE; + goto _exit; + } + + m->state = KMM_MODULE_STATE_RUNNING; + + exit_module = FALSE; + record_failure = FALSE; + + ResetEvent(evt_exit); + +_exit: + if(csp_mod) { + if(record_failure) { + khm_int64 ct; + + i = 0; + khc_read_int32(csp_mod, L"FailureCount", &i); + i++; + khc_write_int32(csp_mod, L"FailureCount", i); + + if(i==1) { /* first fault */ + GetSystemTimeAsFileTime((LPFILETIME) &ct); + khc_write_int64(csp_mod, L"FailureTime", ct); + } + } + khc_close_space(csp_mod); + } + if(csp_mods) + khc_close_space(csp_mods); + + _report_mr2(KHERR_INFO, MSG_IM_MOD_STATE, + _dupstr(m->name), _int32(m->state)); + + if(release_module) + kmm_release_module(kmm_handle_from_module(m)); + + kmmint_remove_from_module_queue(); + + /* if something went wrong after init_module was called on the + module code, we need to call exit_module */ + if(exit_module) + kmm_exit_module(m); + + _end_task(); +} + + +/*! \internal + \brief Uninitializes a module + + \note Should only be called from the context of the registrar + thread */ +void kmm_exit_module(kmm_module_i * m) { + kmm_plugin_i * p; + + /* exiting a module happens in two stages. + + If the module state is running (there are active plugins) then + those plugins must be exited. This has to be done from the + plugin threads. The signal for the plugins to exit must be + issued from the registrar. Therefore, we post messages to the + registrar for each plugin we want to remove and exit + kmm_exit_module(). + + When the last plugin is exited, the plugin management code + automatically signalls the registrar to remove the module. + kmm_exit_module() gets called again. This is the second + stage, where we call exit_module() for the module and start + unloading everything. + */ + + EnterCriticalSection(&cs_kmm); + + /* get rid of any dangling uninitialized plugins */ + LPOP(&(m->plugins), &p); + while(p) { + p->flags &= ~KMM_PLUGIN_FLAG_IN_MODLIST; + kmm_exit_plugin(p); + + /* release hold from kmm_provide_plugin() */ + kmm_release_plugin(kmm_handle_from_plugin(p)); + + LPOP(&(m->plugins), &p); + } + + if(m->state == KMM_MODULE_STATE_RUNNING) { + int np = 0; + + m->state = KMM_MODULE_STATE_EXITPLUG; + + p = kmm_listed_plugins; + + while(p) { + if(p->module == m) { + kmm_hold_plugin(kmm_handle_from_plugin(p)); + kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, KMM_REG_EXIT_PLUGIN, (void *) p); + np++; + } + + p = LNEXT(p); + } + + if(np > 0) { + /* we have to go back and wait for the plugins to exit. + when the last plugin exits, it automatically posts + EXIT_MODULE. We can pick up from there when this + happens. */ + LeaveCriticalSection(&cs_kmm); + return; + } + } + + if(m->flags & KMM_MODULE_FLAG_INITP) + { + exit_module_t p_exit_module; + + if(m->state > 0) + m->state = KMM_MODULE_STATE_EXIT; + + p_exit_module = + (exit_module_t) GetProcAddress(m->h_module, + EXP_EXIT_MODULE); + if(p_exit_module) { + LeaveCriticalSection(&cs_kmm); + p_exit_module(kmm_handle_from_module(m)); + EnterCriticalSection(&cs_kmm); + } + } + + LeaveCriticalSection(&cs_kmm); + + if(m->state > 0) + m->state = KMM_MODULE_STATE_EXITED; + + if(m->h_module) { + FreeLibrary(m->h_module); + } + + if(m->h_resource && (m->h_resource != m->h_module)) { + FreeLibrary(m->h_resource); + } + + m->h_module = NULL; + m->h_resource = NULL; + m->flags = 0; + + /* release the hold obtained in kmm_init_module() */ + kmm_release_module(kmm_handle_from_module(m)); +} diff --git a/src/windows/identity/kmm/kmmconfig.csv b/src/windows/identity/kmm/kmmconfig.csv new file mode 100644 index 000000000..93444bdf4 --- /dev/null +++ b/src/windows/identity/kmm/kmmconfig.csv @@ -0,0 +1,52 @@ +Name,Type,Value,Description +PluginManager,KC_SPACE,0,Plugin Manager Configuration + Plugins,KC_SPACE,0,Plugin Specific configuration + PluginMaxFailureCount,KC_INT32,3,Maximum number of failure counts before plugin is disabled + PluginFailureCountResetTime,KC_INT64,36000,Time after first failure at which the failure count is reset + LoadList,KC_STRING,AfsCred,List of plugins that are active + _Schema,KC_SPACE,0,Plugin schema + Module,KC_STRING,,The name of the module that registered this plugin + Description,KC_STRING,,Description of the plugin + Dependencies,KC_STRING,,Multi string of plugin names of plugins that this plugin depends on + Type,KC_INT32,0,The type of the plugin + Flags,KC_INT32,0,Flags. Currently unused + FailureCount,KC_INT32,0,Number of failed loads + FailureTime,KC_INT64,0,FILETIME of first failure + FailureReason,KC_INT32,0,Reason for first failure. One of the plugin status values. + Parameters,KC_SPACE,0,Plugin parameters. The schema beyond this is plugin dependent. + Parameters,KC_ENDSPACE,0, + _Schema,KC_ENDSPACE,0, + Plugins,KC_ENDSPACE,0, + Modules,KC_SPACE,0,Module Specific configuration + LoadList,KC_STRING,"OpenAFS,MITKrb5,MITKrb4",List of modules to load at startup + ModuleMaxFailureCount,KC_INT32,3,Maximum number of failure counts before module is disabled + ModuleFailureCountResetTime,KC_INT64,72000,Time after first failure at which the failure count is reset + _Schema,KC_SPACE,0,Module schema + ImagePath,KC_STRING,,Path to the DLL + Description,KC_STRING,,Description of the module + Vendor,KC_STRING,,Vendor or copyright string + Flags,KC_INT32,0,Flags. Currently unused. + FailureCount,KC_INT32,0,Number of failed loads + FailureTime,KC_INT64,0,FILETIME of first failure + FailureReason,KC_INT32,0,Reason for first failure. One of the module status values. + FileVersion,KC_INT64,0,khm_version of file + ProductVersion,KC_INT64,0,khm_version of product + PluginList,KC_STRING,,List of plugins implemented in the module + _Schema,KC_ENDSPACE,0, + OpenAFS,KC_SPACE,0,OpenAFS Module + ImagePath,KC_STRING,afscred.dll, + PluginList,KC_STRING,AfsCred, + Vendor,KC_STRING,OpenAFS.org, + OpenAFS,KC_ENDSPACE,0, + MITKrb5,KC_SPACE,0,MIT Kerberos V + ImagePath,KC_STRING,krb5cred.dll, + PluginList,KC_STRING,Krb5Cred, + Vendor,KC_STRING,Massachusetts Institute of Technology, + MITKrb5,KC_ENDSPACE,0, + MITKrb4,KC_SPACE,0,MIT Kerberos IV + ImagePath,KC_STRING,krb4cred.dll, + PluginList,KC_STRING,Krb4Cred, + Vendor,KC_STRING,Massachusetts Institute of Technology, + MITKrb4,KC_ENDSPACE,0, + Modules,KC_ENDSPACE,0, +PluginManager,KC_ENDSPACE,0, diff --git a/src/windows/identity/kmm/kmminternal.h b/src/windows/identity/kmm/kmminternal.h new file mode 100644 index 000000000..662eff228 --- /dev/null +++ b/src/windows/identity/kmm/kmminternal.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KMMINTERNAL_H +#define __KHIMAIRA_KMMINTERNAL_H + +#include +#include + +#define KHERR_FACILITY kmm_facility +#define KHERR_FACILITY_ID KHM_FACILITY_KMM +#define KHERR_HMODULE ((HMODULE) kmm_hInstance) +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +struct kmm_plugin_i_t; /* forward dcl */ + +typedef struct kmm_module_i_t { + khm_int32 magic; + + wchar_t * name; + wchar_t * path; + + wchar_t * vendor; + + HMODULE h_module; + + HMODULE h_resource; + WORD lcid_resource; + + khm_int32 flags; + khm_int32 state; + khm_int32 plugin_count; /* number of active plugins */ + + void * version_info; + + khm_int32 refcount; + + struct kmm_plugin_i_t * plugins; /* only used for registration */ + + LDCL(struct kmm_module_i_t); +} kmm_module_i; + +#define KMM_MODULE_MAGIC 0x482f4e88 + +#define kmm_is_module(m) ((m) && ((kmm_module_i *)m)->magic == KMM_MODULE_MAGIC) + +#define kmm_module_from_handle(m) ((kmm_module_i *) m) +#define kmm_handle_from_module(m) ((kmm_module) m) + +/* the resources have been loaded */ +#define KMM_MODULE_FLAG_RES_LOADED 8 + +/* the signature has been verified */ +#define KMM_MODULE_FLAG_SIG 16 + +/* LoadLibrary succeeded for module */ +#define KMM_MODULE_FLAG_LOADED 1 + +/* init_module entry called */ +#define KMM_MODULE_FLAG_INITP 2 + +/* the module is disabled by the user + (option specifed in configuration) */ +#define KMM_MODULE_FLAG_DISABLED 1024 + +typedef struct kmm_plugin_i_t { + kmm_plugin_reg p; + + khm_int32 magic; + + kmm_module_i * module; + HANDLE ht_thread; + DWORD tid_thread; + + khm_int32 state; + khm_int32 flags; + + int refcount; + + int n_depends; + int n_unresolved; + struct kmm_plugin_i_t * dependants[KMM_MAX_DEPENDANTS]; + int n_dependants; + + LDCL(struct kmm_plugin_i_t); +} kmm_plugin_i; + +#define KMM_PLUGIN_MAGIC 0x320e8fb4 + +#define kmm_is_plugin(p) ((p) && ((kmm_plugin_i *) (p))->magic == KMM_PLUGIN_MAGIC) + +#define kmm_handle_from_plugin(p) ((kmm_plugin) p) +#define kmm_plugin_from_handle(ph) ((kmm_plugin_i *) ph) + +/* the plugin has already been marked for unload */ +#define KMM_PLUGIN_FLAG_UNLOAD 1 + +/* the plugin is disabled by the user + (option specified in configuration) */ +#define KMM_PLUGIN_FLAG_DISABLED 1024 + +/* the plugin is in the kmm_listed_plugins list */ +#define KMM_PLUGIN_FLAG_IN_LIST 2 + +/* the plugin is in the module's plugin list */ +#define KMM_PLUGIN_FLAG_IN_MODLIST 4 + +enum kmm_registrar_uparam_t { + KMM_REG_INIT_MODULE, + KMM_REG_EXIT_MODULE, + KMM_REG_INIT_PLUGIN, + KMM_REG_EXIT_PLUGIN +}; + +extern kmm_module_i * kmm_all_modules; +extern kmm_plugin_i * kmm_listed_plugins; +extern HANDLE ht_registrar; +extern DWORD tid_registrar; +extern DWORD tls_kmm; + +extern hashtable * hash_plugins; +extern hashtable * hash_modules; + +extern CRITICAL_SECTION cs_kmm; +extern int ready; +extern HANDLE evt_startup; +extern HANDLE evt_exit; +extern const wchar_t * kmm_facility; + +extern HINSTANCE kmm_hInstance; + +extern kconf_schema schema_kmmconfig[]; + +/* Registrar */ + +khm_boolean KHMAPI +kmm_reg_cb(khm_int32 msg_type, + khm_int32 msg_sub_type, + khm_ui_4 uparam, + void *vparam); + +DWORD WINAPI kmm_registrar(LPVOID lpParameter); + +DWORD WINAPI kmm_plugin_broker(LPVOID lpParameter); + +void kmm_init_plugin(kmm_plugin_i * p); +void kmm_exit_plugin(kmm_plugin_i * p); +void kmm_init_module(kmm_module_i * m); +void kmm_exit_module(kmm_module_i * m); + +/* Modules */ +kmm_module_i * kmm_get_module_i(wchar_t * name); +kmm_module_i * kmm_find_module_i(wchar_t * name); +void kmm_free_module(kmm_module_i * m); + +/* Plugins */ +kmm_plugin_i * kmm_get_plugin_i(wchar_t * name); +kmm_plugin_i * kmm_find_plugin_i(wchar_t * name); +void kmm_free_plugin(kmm_plugin_i * pi); +void kmm_list_plugin(kmm_plugin_i * p); +void kmm_delist_plugin(kmm_plugin_i * p); + +khm_boolean kmm_load_locale_lib(kmm_module_i * m, kmm_module_locale * l); + +#define KMM_CSNAME_ROOT L"PluginManager" +#define KMM_CSNAME_PLUGINS L"Plugins" +#define KMM_CSNAME_MODULES L"Modules" +#define KMM_VALNAME_LOADLIST L"LoadList" + +void +kmmint_add_to_module_queue(void); + +void +kmmint_remove_from_module_queue(void); + +#define _WAIT_FOR_START \ + do { \ + if(ready) break; \ + WaitForSingleObject(evt_startup, INFINITE); \ + } while(0) + +#endif diff --git a/src/windows/identity/kmm/kmmmain.c b/src/windows/identity/kmm/kmmmain.c new file mode 100644 index 000000000..8ec4bc0a3 --- /dev/null +++ b/src/windows/identity/kmm/kmmmain.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include + +kmm_module_i * kmm_all_modules = NULL; +kmm_plugin_i * kmm_listed_plugins = NULL; + +HANDLE ht_registrar = NULL; +DWORD tid_registrar = 0; +DWORD tls_kmm = 0; + +#define KMM_HASH_SIZE 31 +hashtable * hash_plugins = NULL; +hashtable * hash_modules = NULL; + +CRITICAL_SECTION cs_kmm; +HANDLE evt_startup = NULL; +HANDLE evt_exit = NULL; +int ready = 0; + +HINSTANCE kmm_hInstance; +const wchar_t * kmm_facility = L"KMM"; + +KHMEXP void KHMAPI kmm_init(void) +{ + DWORD dummy; + + EnterCriticalSection(&cs_kmm); + kmm_all_modules = NULL; + kmm_listed_plugins = NULL; + + tls_kmm = TlsAlloc(); + + hash_plugins = hash_new_hashtable( + KMM_HASH_SIZE, + hash_string, + hash_string_comp, + NULL, + NULL); + + hash_modules = hash_new_hashtable( + KMM_HASH_SIZE, + hash_string, + hash_string_comp, + NULL, + NULL); + + ht_registrar = CreateThread( + NULL, + 0, + kmm_registrar, + NULL, + 0, + &dummy); + + _WAIT_FOR_START; + + khc_load_schema(NULL, schema_kmmconfig); + + LeaveCriticalSection(&cs_kmm); +} + +KHMEXP void KHMAPI kmm_exit(void) +{ + kmm_module_i * m; + kmm_plugin_i * p; + + EnterCriticalSection(&cs_kmm); + + p = kmm_listed_plugins; + while(p) { + kmm_plugin_i * pn; + + pn = LNEXT(p); + /* plugins that were never resolved should be kicked off + the list. Flipping the refcount will do that if no + other references exist for the plugin */ + if(p->state == KMM_PLUGIN_STATE_PLACEHOLDER) { + kmm_hold_plugin(kmm_handle_from_plugin(p)); + kmm_release_plugin(kmm_handle_from_plugin(p)); + } + + p = pn; + } + + m = kmm_all_modules; + while(m) { + kmm_unload_module(kmm_handle_from_module(m)); + m = LNEXT(m); + } + + LeaveCriticalSection(&cs_kmm); + WaitForSingleObject(evt_exit, INFINITE); + EnterCriticalSection(&cs_kmm); + + kmq_post_thread_quit_message(tid_registrar, 0, NULL); + + hash_del_hashtable(hash_plugins); + hash_del_hashtable(hash_modules); + + LeaveCriticalSection(&cs_kmm); + + TlsFree(tls_kmm); + + tls_kmm = 0; +} + +void kmm_dll_init(void) +{ + InitializeCriticalSection(&cs_kmm); + evt_startup = CreateEvent(NULL, TRUE, FALSE, NULL); + evt_exit = CreateEvent(NULL, TRUE, TRUE, NULL); +} + +void kmm_dll_exit(void) +{ + DeleteCriticalSection(&cs_kmm); + if(evt_startup) + CloseHandle(evt_startup); + evt_startup = NULL; +} + +void +kmm_process_attach(HINSTANCE hinstDLL) { + kmm_hInstance = hinstDLL; + kmm_dll_init(); +} + +void +kmm_process_detach(void) { + kmm_dll_exit(); +} + diff --git a/src/windows/identity/kmm/kplugin.h b/src/windows/identity/kmm/kplugin.h new file mode 100644 index 000000000..f7489bf4c --- /dev/null +++ b/src/windows/identity/kmm/kplugin.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KPLUGIN_H +#define __KHIMAIRA_KPLUGIN_H + +#include +#include + +/*! \addtogroup kmm +@{*/ +/*! \defgroup kplugin NetIDMgr Plugin Callbacks + +See the following related documentation pages for more information +about NetIDMgr plugins. + +These are prototypes of functions that must be implemented by a NetIDMgr +plugin. + +- \ref plugins +@{*/ + +/*! \brief Initialize the module + + This is the first callback function to be called in a module. + Perform all the required intialization when this is called. As + mentioned in \ref plugins, you should not attempt to call any + NetIDMgr API function from DLLMain or other initialization code + other than this one. + + You should use this call back to register the plugins that will be + implemented in this module and to notify the plugin manager of any + resource libraries that this module will use. + + Call: + - kmm_set_locale() : to set the notify the plugin manager of the + locale specifc resource libraries that are used by this module. + - kmm_provide_plugin() : to register each plugin that is + implemented in this module. + + This function is called in the context of the current user, from + the plug-in manager thread. This same thread is used by the + plug-in manager to load and initialize all the modules for a + session. + + The name of the callback must be init_module(). The calling + convention is KHMAPI, which is currently __stdcall. + + If this function does not register any plugins, the plugin manager + will immediately call exit_module() and unload the module even if + the init_module() function completes successfully. + + \return Return the following values to indicate whether the module + successfully initialized or not. + - KHM_ERROR_SUCCESS : Succeeded. The module manager will call + init_plugin() for each of the registered plugins for the + module. + - any other error code: Signals that the module did not + successfully initialize. The plugin manager will + immediately call exit_module() and then unload the module. + + \note This callback is required. +*/ +KHMEXP khm_int32 KHMAPI init_module(kmm_module h_module); + +/*! \brief Type for init_module() */ +typedef khm_int32 (KHMAPI *init_module_t)(kmm_module); + +#if defined(_WIN64) +#define EXP_INIT_MODULE "_init_module@8" +#elif defined(_WIN32) +#define EXP_INIT_MODULE "_init_module@4" +#else +#error EXP_INIT_MODULE not defined for platform +#endif + +/*! \brief Plugin procedure + + This is the message processor for a plugin. See \ref pi_fw_pnm_p + for more information. + + Essentially, this is a message subscriber for KMQ messages. +*/ +KHMEXP khm_int32 KHMAPI _plugin_proc(khm_int32 msg_type, khm_int32 msg_subtype, khm_ui_4 uparam, void * vparam); + +/*! \brief Type for init_plugin() */ +typedef kmq_callback_t _plugin_proc_t; + +/*! \brief Exit a module + + This is the last callback function that the NetIDMgr module + manager calls before unloading the module. When this function is + called, all of the plugins for the module have already been + stopped. However, any localization libraries that were loaded as + a result of init_module() calling kmm_set_locale_info() will still + be loaded. These localization libraries will be unloaded + immediately after this callback returns. + + Use this callback to perform any required cleanup tasks. However, + it is advisable that each plugin perform its own cleanup tasks, + since each plugin may be stopped independently of others. + + \return The return value of this function is ignored. + + \note This callback is not required. +*/ +KHMEXP khm_int32 KHMAPI exit_module(kmm_module h_module); + +/*! \brief Type for exit_module() */ +typedef khm_int32 (KHMAPI *exit_module_t)(kmm_module); + +#if defined(_WIN64) +#define EXP_EXIT_MODULE "_exit_module@8" +#elif defined(_WIN32) +#define EXP_EXIT_MODULE "_exit_module@4" +#else +#error EXP_EXIT_MODULE not defined for platform +#endif + +/*@}*/ +/*@}*/ + +#endif diff --git a/src/windows/identity/kmm/lang/kmm_msgs.mc b/src/windows/identity/kmm/lang/kmm_msgs.mc new file mode 100644 index 000000000..5e88121d1 --- /dev/null +++ b/src/windows/identity/kmm/lang/kmm_msgs.mc @@ -0,0 +1,146 @@ +; // ** kmm_msgs.mc + +; /* Since .mc files can contain strings from any language, we define +; all our messages in one file in the /lang/ directory instead of +; language specific subdirectories. */ + +; /* The type is set to (wchar_t *) because that's what we will be +; feeding kherr_report() function. */ + +MessageIdTypedef=LPWSTR + +; /* Severity values as defined in the message definition file are +; currently ignored. */ + +SeverityNames=( + Success=0x0 +) + +LanguageNames=( + English=0x409:MSG_ENU +) + +OutputBase=16 + +; /* Actual messages start here */ + +MessageId=1 +Severity=Success +SymbolicName=MSG_INITIAL +Language=English +Initial placeholder message +. + +MessageId= +SymbolicName=MSG_LOAD_DEFAULT +Language=English +Load default modules +. + +MessageId= +SymbolicName=MSG_INIT_MODULE +Language=English +Initializing module [%1] +. + +MessageId= +SymbolicName=MSG_IM_GET_CONFIG +Language=English +Can't get configuration for modules +. + +MessageId= +SymbolicName=MSG_IM_NOT_PREINIT +Language=English +Module is not in PREINIT state. Current state=[%1!d!] +. + +MessageId= +SymbolicName=MSG_IM_NOT_REGISTERED +Language=English +Module is not registered +. + +MessageId= +SymbolicName=MSG_IM_DISABLED +Language=English +Module is disabled +. + +MessageId= +SymbolicName=MSG_IM_MAX_FAIL +Language=English +Module has failed too many times +. + +Messageid= +SymbolicName=MSG_IM_NOT_FOUND +Language=English +Module binary was not found. Checked path [%1] +. + +MessageId= +SymbolicName=MSG_IM_NO_ENTRY +Language=English +Entry point not found. Checked entry point [%1] +. + +MessageId= +SymbolicName=MSG_IM_INIT_FAIL +Language=English +Module initialization entry point returned failure code [%1!d!] +. + +MessageId= +SymbolicName=MSG_IM_NO_PLUGINS +Language=English +No plugins were registerd by the module +. + +MessageId= +SymbolicName=MSG_IM_MOD_STATE +Language=English +Module [%1] is in state [%2!d!] +. + +MessageId= +SymbolicName=MSG_IP_TASK_DESC +Language=English +Initializing plugin [%1] +. + +MessageId= +SymbolicName=MSG_IP_GET_CONFIG +Language=English +Can't get configuration for plugins +. + +MessageId= +SymbolicName=MSG_IP_NOT_REGISTERED +Language=English +The plugin is not registered +. + +MessageId= +SymbolicName=MSG_IP_DISABLED +Language=English +The plugin is disabled +. + +MessageId= +SymbolicName=MSG_IP_HOLD +Language=English +Placing plugin [%1] on hold +. + +MessageId= +SymbolicName=MSG_IP_STATE +Language=English +Leaving plugin [%1] in state [%2!d!] +. + +MessageId= +SymbolicName=MSG_IP_EXITING +Language=English +The plugin [%1] is in error state [%2!d!]. Exiting plugin. +. diff --git a/src/windows/identity/kmq/Makefile b/src/windows/identity/kmq/Makefile new file mode 100644 index 000000000..1f11e0fe4 --- /dev/null +++ b/src/windows/identity/kmq/Makefile @@ -0,0 +1,48 @@ +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +MODULE=kmq +!include <../config/Makefile.w32> + +INCFILES= \ + $(INCDIR)\kmq.h + +OBJFILES= \ + $(OBJ)\kmqmain.obj \ + $(OBJ)\init.obj \ + $(OBJ)\msgtype.obj \ + $(OBJ)\consumer.obj \ + $(OBJ)\publisher.obj \ + $(OBJ)\kmqconfig.obj + +SDKLIBFILES=\ + strsafe.lib + +$(OBJ)\kmqconfig.c: kmqconfig.csv $(CONFDIR)\csvschema.cfg + $(CCSV) $** $@ + +all: mkdirs $(INCFILES) $(OBJFILES) + +clean:: + $(RM) $(INCFILES) diff --git a/src/windows/identity/kmq/consumer.c b/src/windows/identity/kmq/consumer.c new file mode 100644 index 000000000..32072cf63 --- /dev/null +++ b/src/windows/identity/kmq/consumer.c @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include + +DWORD kmq_tls_queue; + +CRITICAL_SECTION cs_kmq_msg_ref; + +kmq_message_ref * kmq_msg_ref_free = NULL; + +/* ad-hoc subscriptions */ +kmq_msg_subscription * kmq_adhoc_subs = NULL; + +/*! \internal + \brief Get a message ref object + \note called with cs_kmq_msg_ref held */ +kmq_message_ref * kmqint_get_message_ref(void) { + kmq_message_ref * r; + + LPOP(&kmq_msg_ref_free, &r); + if(!r) { + r = malloc(sizeof(kmq_message_ref)); + } + ZeroMemory(r, sizeof(kmq_message_ref)); + + r->msg = NULL; + r->recipient = NULL; + + return r; +} + +/*! \internal + \brief Free a message ref object + \note called with cs_kmq_msg_ref and cs_kmq_msg held */ +void kmqint_put_message_ref(kmq_message_ref * r) { + if(!r) + return; + if(r->msg) { + r->msg->refcount--; + r->msg = NULL; + } + LPUSH(&kmq_msg_ref_free, r); +} + +/*! \internal + \brief Get the queue associated with the current thread + \note Obtains ::cs_kmq_global + */ +kmq_queue * kmqint_get_thread_queue(void) { + kmq_queue * q; + + q = (kmq_queue *) TlsGetValue(kmq_tls_queue); + if(!q) { + kmqint_attach_this_thread(); + q = (kmq_queue *) TlsGetValue(kmq_tls_queue); + } + + return q; +} + +/*! \internal + \brief Get the topmost message ref for a queue + \note Obtains kmq_queue::cs + */ +void kmqint_get_queue_message_ref(kmq_queue * q, kmq_message_ref ** r) { + EnterCriticalSection(&q->cs); + QGET(q,r); + if(QTOP(q)) + SetEvent(q->wait_o); + LeaveCriticalSection(&q->cs); +} + +/*! \internal + \brief Post a message to a queue + \note Obtains ::cs_kmq_msg_ref, ::cs_kmq_msg, kmq_queue::cs + */ +void kmqint_post_queue(kmq_queue * q, kmq_message *m) { + kmq_message_ref *r; + + EnterCriticalSection(&cs_kmq_msg_ref); + r = kmqint_get_message_ref(); + LeaveCriticalSection(&cs_kmq_msg_ref); + + r->msg = m; + r->recipient = NULL; + + EnterCriticalSection(&cs_kmq_msg); + m->refcount++; + m->nSent++; + LeaveCriticalSection(&cs_kmq_msg); + + EnterCriticalSection(&q->cs); + QPUT(q,r); + SetEvent(q->wait_o); + LeaveCriticalSection(&q->cs); +} + +/*! \internal + \brief Post a message to a subscriber + \note Obtains ::cs_kmq_msg_ref, ::cs_kmq_msg, kmq_queue::cs + \note Should be called with ::cs_kmq_msg held + */ +void kmqint_post(kmq_msg_subscription * s, kmq_message * m, khm_boolean try_send) { + if(s->rcpt_type == KMQ_RCPTTYPE_CB) { + kmq_queue *q; + kmq_message_ref *r; + + q = s->queue; + + if(try_send && q->thread == GetCurrentThreadId()) { + khm_int32 rv; + /* we are sending a message from this thread to this thread. + just call the recipient directly, bypassing the message queue. */ + m->refcount++; + m->nSent++; + rv = s->recipient.cb(m->type, m->subtype, m->uparam, m->vparam); + m->refcount--; + if(KHM_SUCCEEDED(rv)) + m->nCompleted++; + else + m->nFailed++; + } else { + EnterCriticalSection(&cs_kmq_msg_ref); + r = kmqint_get_message_ref(); + LeaveCriticalSection(&cs_kmq_msg_ref); + + r->msg = m; + r->recipient = s->recipient.cb; + + m->refcount++; + m->nSent++; + + EnterCriticalSection(&q->cs); + QPUT(q,r); + SetEvent(q->wait_o); + LeaveCriticalSection(&q->cs); + } + } + +#ifdef _WIN32 + else if(s->rcpt_type == KMQ_RCPTTYPE_HWND) { + m->refcount++; + + if(try_send && GetCurrentThreadId() == GetWindowThreadProcessId(s->recipient.hwnd, NULL)) { + /* kmqint_post does not know whether there are any other messages + waiting to be posted at this point. Hence, simply sending the + message is not the right thing to do as the recipient may + incorrectly assume that the message has completed when + (m->nCompleted + m->nFailed == m->nSent). Therefore, we only + increment nSent after the message is sent. */ + SendMessage(s->recipient.hwnd, KMQ_WM_DISPATCH, m->type, (LPARAM) m); + m->nSent++; + } else { + m->nSent++; + PostMessage(s->recipient.hwnd, KMQ_WM_DISPATCH, m->type, (LPARAM) m); + } + } +#endif + + else { + /* This could either be because we were passed in an invalid subscription + or because we lost a race to a thread that deleted an ad-hoc + subscription. */ +#ifdef DEBUG + assert(FALSE); +#else + return; +#endif + } +} + +/*! \internal + \brief Subscribes a window to a message type + \note Obtains ::cs_kmq_types + */ +KHMEXP khm_int32 KHMAPI kmq_subscribe_hwnd(khm_int32 type, HWND hwnd) { + kmq_msg_subscription * s; + + s = malloc(sizeof(kmq_msg_subscription)); + LINIT(s); + s->queue = NULL; + s->rcpt_type = KMQ_RCPTTYPE_HWND; + s->recipient.hwnd = hwnd; + kmqint_msg_type_add_sub(type, s); + + return KHM_ERROR_SUCCESS; +} + +/*! \internal + \note Obtains ::cs_kmq_types, ::cs_kmq_global + */ +KHMEXP khm_int32 KHMAPI kmq_subscribe(khm_int32 type, kmq_callback_t cb) { + kmq_msg_subscription * s; + + s = malloc(sizeof(kmq_msg_subscription)); + LINIT(s); + s->queue = kmqint_get_thread_queue(); + s->rcpt_type = KMQ_RCPTTYPE_CB; + s->recipient.cb = cb; + kmqint_msg_type_add_sub(type, s); + + return KHM_ERROR_SUCCESS; +} + +/*! \internal + \note Obtains ::cs_kmq_global +*/ +KHMEXP khm_int32 KHMAPI kmq_create_subscription(kmq_callback_t cb, khm_handle * result) +{ + kmq_msg_subscription * s; + + s = malloc(sizeof(kmq_msg_subscription)); + LINIT(s); + s->queue = kmqint_get_thread_queue(); + s->rcpt_type = KMQ_RCPTTYPE_CB; + s->recipient.cb = cb; + + EnterCriticalSection(&cs_kmq_global); + LPUSH(&kmq_adhoc_subs, s); + LeaveCriticalSection(&cs_kmq_global); + + *result = (khm_handle) s; + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kmq_delete_subscription(khm_handle sub) +{ + kmq_msg_subscription * s; + + s = (kmq_msg_subscription *) sub; + + s->type = 0; + + EnterCriticalSection(&cs_kmq_global); + LDELETE(&kmq_adhoc_subs, s); + LeaveCriticalSection(&cs_kmq_global); + + free(s); + + return KHM_ERROR_SUCCESS; +} + +/*! \internal + \brief Unsubscribes a window from a message type + \note Obtains ::cs_kmq_types + */ +KHMEXP khm_int32 KHMAPI kmq_unsubscribe_hwnd(khm_int32 type, HWND hwnd) { + kmq_msg_subscription * s; + + s = kmqint_msg_type_del_sub_hwnd(type, hwnd); + if(s) + free(s); + return (s)?KHM_ERROR_SUCCESS:KHM_ERROR_NOT_FOUND; +} + +/*! \internal + \brief Unsubscribe a callback from a message type + \note Obtains ::cs_kmq_types, ::cs_kmq_global + */ +KHMEXP khm_int32 KHMAPI kmq_unsubscribe(khm_int32 type, kmq_callback_t cb) { + kmq_msg_subscription * s; + + s = kmqint_msg_type_del_sub_cb(type,cb); + if(s) + free(s); + + return (s)?KHM_ERROR_SUCCESS:KHM_ERROR_NOT_FOUND; +} + +KHMEXP LRESULT KHMAPI kmq_wm_begin(LPARAM lparm, kmq_message ** m) { + *m = (kmq_message *) lparm; + if ((*m)->err_ctx) { + kherr_push_context((*m)->err_ctx); + } + return TRUE; +} + +/*! \internal + \note Obtains ::cs_kmq_msg + */ +KHMEXP LRESULT KHMAPI kmq_wm_end(kmq_message *m, khm_int32 rv) { + if (m->err_ctx) + kherr_pop_context(); + + EnterCriticalSection(&cs_kmq_msg); + m->refcount--; + if(KHM_SUCCEEDED(rv)) + m->nCompleted++; + else + m->nFailed++; + + if(m->nCompleted + m->nFailed == m->nSent) { + kmqint_put_message(m); + } + LeaveCriticalSection(&cs_kmq_msg); + + return TRUE; +} + +/*! \internal + \note Obtains ::cs_kmq_msg + */ +KHMEXP LRESULT KHMAPI kmq_wm_dispatch(LPARAM lparm, kmq_callback_t cb) { + kmq_message *m; + khm_int32 rv; + + m = (kmq_message *) lparm; + + if (m->err_ctx) + kherr_push_context(m->err_ctx); + + rv = cb(m->type, m->subtype, m->uparam, m->vparam); + + if (m->err_ctx) + kherr_pop_context(); + + EnterCriticalSection(&cs_kmq_msg); + + m->refcount--; + if(KHM_SUCCEEDED(rv)) + m->nCompleted++; + else + m->nFailed++; + + if(m->nCompleted + m->nFailed == m->nSent) { + kmqint_put_message(m); + } + LeaveCriticalSection(&cs_kmq_msg); + + return TRUE; +} + +/*! \internal + \note Obtains ::cs_kmq_global, kmq_queue::cs, ::cs_kmq_msg_ref, ::cs_kmq_msg, +*/ +KHMEXP khm_int32 KHMAPI kmq_dispatch(kmq_timer timeout) { + kmq_queue * q; + kmq_message_ref * r; + kmq_message *m; + DWORD hr; + + q = kmqint_get_thread_queue(); + + hr = WaitForSingleObject(q->wait_o, timeout); + if(hr == WAIT_OBJECT_0) { + /* signalled */ + kmqint_get_queue_message_ref(q, &r); + + m = r->msg; + + if(m->type != KMSG_SYSTEM || m->subtype != KMSG_SYSTEM_EXIT) { + khm_boolean rv; + + if (m->err_ctx) + kherr_push_context(m->err_ctx); + + /* dispatch */ + rv = r->recipient(m->type, m->subtype, m->uparam, m->vparam); + + if (m->err_ctx) + kherr_pop_context(); + + EnterCriticalSection(&cs_kmq_msg); + EnterCriticalSection(&cs_kmq_msg_ref); + kmqint_put_message_ref(r); + LeaveCriticalSection(&cs_kmq_msg_ref); + + if(KHM_SUCCEEDED(rv)) + m->nCompleted++; + else + m->nFailed++; + + if(m->nCompleted + m->nFailed == m->nSent) { + kmqint_put_message(m); + } + LeaveCriticalSection(&cs_kmq_msg); + + return KHM_ERROR_SUCCESS; + } else { + EnterCriticalSection(&cs_kmq_msg); + EnterCriticalSection(&cs_kmq_msg_ref); + kmqint_put_message_ref(r); + LeaveCriticalSection(&cs_kmq_msg_ref); + m->nCompleted++; + if(m->nCompleted + m->nFailed == m->nSent) { + kmqint_put_message(m); + } + LeaveCriticalSection(&cs_kmq_msg); + + return KHM_ERROR_EXIT; + } + } else { + return KHM_ERROR_TIMEOUT; + } +} + +/* TODO: rename this file to subscriber.c */ diff --git a/src/windows/identity/kmq/init.c b/src/windows/identity/kmq/init.c new file mode 100644 index 000000000..cb50c5476 --- /dev/null +++ b/src/windows/identity/kmq/init.c @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include +#include + +CRITICAL_SECTION cs_kmq_global; +kmq_timer kmq_queue_dead_timeout; +kmq_timer kmq_call_dead_timeout; + +kmq_queue * queues; + +LONG kmq_init_once = 0; + +void kmqint_init(void) { + khm_handle hconfig = NULL; + + queues = NULL; + + InitializeCriticalSection(&cs_kmq_global); + InitializeCriticalSection(&cs_kmq_msg); + InitializeCriticalSection(&cs_kmq_msg_ref); + + EnterCriticalSection(&cs_kmq_global); + khc_load_schema(NULL, schema_kmqconfig); + khc_open_space(NULL, KMQ_CONF_SPACE_NAME, KHM_PERM_READ, &hconfig); + if(hconfig) { + khc_read_int32(hconfig, KMQ_CONF_QUEUE_DEAD_TIMEOUT_NAME, &kmq_queue_dead_timeout); + khc_read_int32(hconfig, KMQ_CONF_CALL_DEAD_TIMEOUT_NAME, &kmq_call_dead_timeout); + khc_close_space(hconfig); + } + kmqint_init_msg_types(); + LeaveCriticalSection(&cs_kmq_global); + + kmq_tls_queue = TlsAlloc(); +} + +void kmqint_exit(void) { + EnterCriticalSection(&cs_kmq_global); + kmqint_exit_msg_types(); + LeaveCriticalSection(&cs_kmq_global); + DeleteCriticalSection(&cs_kmq_msg); + DeleteCriticalSection(&cs_kmq_msg_ref); + DeleteCriticalSection(&cs_kmq_global); + + TlsFree(kmq_tls_queue); +} + +/*! \internal + \brief Preps a thread for use with kmq + \note Obtains ::cs_kmq_global + */ +void kmqint_attach_this_thread(void) { + kmq_queue * q; + + q = (kmq_queue *) TlsGetValue(kmq_tls_queue); + if(!q) { + EnterCriticalSection(&cs_kmq_global); + + q = malloc(sizeof(kmq_queue)); + + InitializeCriticalSection(&q->cs); + q->thread = GetCurrentThreadId(); + QINIT(q); + LINIT(q); + q->wait_o = CreateEvent(NULL, FALSE, FALSE, NULL); + q->load = 0; + q->last_post = 0; + + LPUSH(&queues, q); + + TlsSetValue(kmq_tls_queue, (LPVOID) q); + + LeaveCriticalSection(&cs_kmq_global); + } +} + +/*! \internal + \brief Detaches the current thread from kmq + \note Obtains ::cs_kmq_global + */ +void kmqint_detach_this_thread(void) { + kmq_queue * q; + + q = (kmq_queue *) TlsGetValue(kmq_tls_queue); + if(q) { + EnterCriticalSection(&cs_kmq_global); + + LDELETE(&queues, q); + + DeleteCriticalSection(&q->cs); + CloseHandle(q->wait_o); + + /* TODO: free up the queued messages */ + + TlsSetValue(kmq_tls_queue, (LPVOID) 0); + + LeaveCriticalSection(&cs_kmq_global); + } +} + +HANDLE kmq_h_compl = NULL; +kmq_thread_id kmq_tid_compl; + +/* Message transfer */ +struct tag_kmq_msg_xfer { + QDCL(kmq_message); +} kmq_completion_xfer; + +HANDLE compl_wx; +BOOL compl_continue; +CRITICAL_SECTION cs_compl; + +DWORD WINAPI kmqint_completion_thread_proc(LPVOID p) { + kmq_message * m; + kherr_context * ctx; + + EnterCriticalSection(&cs_compl); + do { + + if (QTOP(&kmq_completion_xfer) == NULL) { + LeaveCriticalSection(&cs_compl); + WaitForSingleObject(compl_wx, INFINITE); + EnterCriticalSection(&cs_compl); + /* go through the loop again before checking the queue */ + } else { + QGET(&kmq_completion_xfer, &m); + LeaveCriticalSection(&cs_compl); + EnterCriticalSection(&cs_kmq_msg); + + ctx = m->err_ctx; + + if (ctx) + kherr_push_context(ctx); + + kmqint_put_message(m); + + if (ctx) + kherr_pop_context(); + + LeaveCriticalSection(&cs_kmq_msg); + EnterCriticalSection(&cs_compl); + } + + } while(compl_continue); + + LeaveCriticalSection(&cs_compl); + + ExitThread(0); + + /* not reached */ + return 0; +} + +int kmqint_call_completion_handler(kmq_msg_completion_handler h, + kmq_message * m) { + if (h == NULL) + return 0; + + /* We only dispatch to the completion thread if we are not the + completion thread. If calling the completion handler results + in more messages completing, then we just call the completion + handler directly. We also make an exception for completions + that happen before the message queue is properly intiailized. */ + + if (kmq_tid_compl != GetCurrentThreadId() && + kmq_h_compl != NULL) { + + EnterCriticalSection(&cs_compl); + QPUT(&kmq_completion_xfer, m); + SetEvent(compl_wx); + LeaveCriticalSection(&cs_compl); + + return 1; + + } else { + h(m); + + return 0; + } +} + +KHMEXP khm_int32 KHMAPI kmq_init(void) { + if (InterlockedIncrement(&kmq_init_once) == 1) { + EnterCriticalSection(&cs_kmq_global); + + InitializeCriticalSection(&cs_compl); + compl_wx = CreateEvent(NULL, FALSE, FALSE, NULL); + compl_continue = TRUE; + QINIT(&kmq_completion_xfer); + + kmq_h_compl = CreateThread(NULL, + 0, + kmqint_completion_thread_proc, + NULL, + 0, + &kmq_tid_compl); + + assert(kmq_h_compl != NULL); + + LeaveCriticalSection(&cs_kmq_global); + } + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kmq_exit(void) { + if (InterlockedDecrement(&kmq_init_once) == 0) { + + EnterCriticalSection(&cs_compl); + compl_continue = FALSE; + SetEvent(compl_wx); + LeaveCriticalSection(&cs_compl); + + WaitForSingleObject(kmq_h_compl, INFINITE); + + EnterCriticalSection(&cs_kmq_global); + CloseHandle(kmq_h_compl); + kmq_h_compl = NULL; + kmq_tid_compl = 0; + CloseHandle(compl_wx); + DeleteCriticalSection(&cs_compl); + LeaveCriticalSection(&cs_kmq_global); + } + + return KHM_ERROR_SUCCESS; +} diff --git a/src/windows/identity/kmq/kmq.h b/src/windows/identity/kmq/kmq.h new file mode 100644 index 000000000..3d596d795 --- /dev/null +++ b/src/windows/identity/kmq/kmq.h @@ -0,0 +1,743 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KMQ_H__ +#define __KHIMAIRA_KMQ_H__ + +/*! \defgroup kmq NetIDMgr Message Queue */ +/*@{*/ + +#include +#include +#include + +/* general */ +#ifdef _WIN32 +typedef DWORD kmq_thread_id; +typedef DWORD kmq_timer; +#endif + +#ifdef _WIN32 +/*! \brief Window message for kmq + + This message is sent to the window procedure of a window if that + window is a subscriber to KMQ messages. + + \see kmq_subscribe_hwnd() for more information about handling this + window message + */ +#define KMQ_WM_DISPATCH (WM_APP+0x100) +#endif + +/* callback */ + +/*! \brief A message callback + + Should return TRUE if the message is properly handled. Otherwise + return FALSE */ +typedef khm_int32 (KHMAPI *kmq_callback_t)(khm_int32 msg_type, + khm_int32 msg_sub_type, + khm_ui_4 uparam, + void * vparam); + +/* message */ + +/*! \brief A single response. + + Certain broadcast messages may user scatter-gather type + notification and result gathering. Individual subscribers to a + message attach their individual responses to a ::kmq_response + object and attach that to the message which can later be read by + the sender of the message. + */ +typedef struct tag_kmq_response { + kmq_thread_id thread; + void * response; + + LDCL(struct tag_kmq_response); +} kmq_response; + +/*! \brief A single message + */ +typedef struct tag_kmq_message { + khm_int32 type; /*!< Type of message */ + khm_int32 subtype; /*!< Subtype of message */ + + khm_ui_4 uparam; /*!< Integer parameter */ + void * vparam; /*!< Pointer to parameter blob */ + + khm_int32 nSent; /*!< Number of instances of message + sent (for broadcast messages) */ + + khm_int32 nCompleted; /*!< Number of instances that have + completed processing (for broadcast + messages) */ + + khm_int32 nFailed; /*!< Number of instances that failed + to process (for broadcast + messages) */ + + kmq_response * responses; /*!< List of responses */ + HANDLE wait_o; /*!< Event to wait on (only valid if + the publisher of the message + requested a handle to the call) */ + + kmq_timer timeSent; /*!< Time at which the message was + sent */ + kmq_timer timeExpire; /*!< Time at which the message + expires */ + + kherr_context * err_ctx; /*!< Error context for the message */ + + khm_int32 refcount; + + LDCL(struct tag_kmq_message); +} kmq_message; + +/*! \brief A handle to a call + */ +typedef kmq_message *kmq_call; + +/*! \brief Message reference */ +typedef struct tag_kmq_message_ref { + kmq_message * msg; /*!< Message that we are referring + to */ + kmq_callback_t recipient; /*!< The recipient of the message */ + + LDCL(struct tag_kmq_message_ref); +} kmq_message_ref; + +/*! \brief Message queue + + Each thread gets its own message queue. When a message is + broadcast to which there is a subscriber in a particular thread, a + reference to the message is placed in the message queue of the + thread. The dispatch procedure then dispatches the message as + described in the message reference. +*/ +typedef struct tag_kmq_queue { + kmq_thread_id thread; /*!< The thread id */ + + CRITICAL_SECTION cs; + HANDLE wait_o; + + khm_int32 load; /*!< Number of messages waiting to be + processed on this message queue. */ + kmq_timer last_post; /*!< Time the last message was + received */ + + /*Q*/ + QDCL(kmq_message_ref); /*!< Queue of message references */ + + /*Lnode*/ + LDCL(struct tag_kmq_queue); +} kmq_queue; + +/*! \brief Message subscription + + A subscription binds a recipient with a message type. These are + specific to a thread. I.e. a subscription that was made in one + thread will not receive messages in the context of another thread. +*/ +typedef struct tag_kmq_msg_subscription { + khm_int32 type; /*!< Type of message */ + khm_int32 rcpt_type; /*!< Type of recipient. One of + ::KMQ_RCPTTYPE_CB or + ::KMQ_RCPTTYPE_HWND */ + union { + kmq_callback_t cb; /*!< Callback if the subscription is + of callback type */ + HWND hwnd; /*!< Window handle if the subscription + is a windows message type */ + } recipient; + + kmq_queue * queue; /*!< Associated queue */ + + /*lnode*/ + LDCL(struct tag_kmq_msg_subscription); +} kmq_msg_subscription; + +/*! \brief Callback recipient type + + The recipient is a callback function */ +#define KMQ_RCPTTYPE_CB 1 + +/*! \brief Windows recipient type + + The recipient is a window */ +#define KMQ_RCPTTYPE_HWND 2 + +/* publishers */ + +/*! \brief A completion handler for a message + + Each message type can have a completion handler. Once a message + of this a specific type has been broadcast and handled by all the + subscripbers, the message will be passed down to the completion + handler before the associated data structures are freed. This + allows applications that define message type to also define clean + up for each message. For example, the completion handler can + initiate another message if the messages form a sequence or free + up blocks of memory that was passed as the parameter to the + message. + */ +typedef void (KHMAPI *kmq_msg_completion_handler)(kmq_message *); + +/*! \brief A message type + */ +typedef struct tag_kmq_msg_type { + khm_int32 id; /*!< Identifier for the message + type. */ + kmq_msg_subscription * subs; /*!< The list of subscriptions */ + kmq_msg_completion_handler completion_handler; /*!< Completion + handler for the message type */ + + wchar_t * name; /*!< Name of the message type for + named types. Message type names are + language independant. */ + + /*Lnode*/ + LDCL(struct tag_kmq_msg_type); +} kmq_msg_type; + +/*! \brief The maximum number of message types + */ +#define KMQ_MSG_TYPE_MAX 255 + +/*! \brief Maximum number of characters in a message type name + + The count includes the terminating NULL + */ +#define KMQ_MAXCCH_TYPE_NAME 256 + +/*! \brief Maximum number of bytes in a message type name + + Type count includes the terminating NULL + */ +#define KMQ_MAXCB_TYPE_NAME (KMQ_MAXCCH_TYPE_NAME * sizeof(wchar_t)) + +KHMEXP khm_int32 KHMAPI kmq_init(void); + +KHMEXP khm_int32 KHMAPI kmq_exit(void); + +/*! \brief Register a message type + + Registers a custom message type. The \a name parameter specifies + a language independent name for the message type and must be + unique and must be less than ::KMQ_MAXCCH_TYPE_NAME characters. + + \param[in] name Name of the message type. Upto + ::KMQ_MAXCCH_TYPE_NAME characters including terminating NULL. + The \a name cannot be a zero length string. + + \param[out] new_id Receives the new message type ID. Specify NULL + if the new message type is not required. + + \see kmq_find_type() and kmq_unregister_type() + + \retval KHM_ERROR_INVALID_PARM The \a name parameter was invalid. + \retval KHM_ERROR_EXISTS A message type with that name already exists. + \retval KHM_ERROR_NO_RESOURCES Can't register any more message types. + \retval KHM_ERROR_SUCCESS The operation succeeded. + */ +KHMEXP khm_int32 KHMAPI kmq_register_type(wchar_t * name, khm_int32 * new_id); + +/*! \brief Find a message type + + Find the message type with the given name. If found, the type ID + is returned in \a id. + + \retval KHM_ERROR_SUCCESS A message type with the given name was + found. + \retval KHM_ERROR_NOT_FOUND A message type with the given name was + not found. + */ +KHMEXP khm_int32 KHMAPI kmq_find_type(wchar_t * name, khm_int32 * id); + +/*! \brief Unregister a message type + + Unregisters a message type that was registered using + kmq_register_type(). + + \retval KHM_ERROR_SUCCESS The specified message type was + successfully unregistered. + + \retval KHM_ERROR_NOT_FOUND The message type was not found. + */ +KHMEXP khm_int32 KHMAPI kmq_unregister_type(khm_int32 id); + +/*! \brief Subscribte to a message type. + + Adds a subscription to messages of type \a type. Subscriptions + are managed per thread. Therefore the subscription is actually + added to the subscription list for the current thread (the thread + which calls kmq_subscribe()). + + When a message of type \a type is received by the thread, it is + dispatched to the callback function identified by \a cb within the + context of this thread. + + \note Calling kmq_subscribe() from within multiple threads with + the same \a type and \a cb will result in multiple + subscriptions. + + \see kmq_unsubscribe() + \see kmq_dispatch() +*/ +KHMEXP khm_int32 KHMAPI kmq_subscribe(khm_int32 type, kmq_callback_t cb); + +/*! \brief Subscribe a window to a message type + + Adds the window specified by \a hwnd to the subscription list for + the message type \a type. When a message of this type is posted, + then the window procedure of the window \a hwnd receives a message + ::KMQ_WM_DISPATCH. + + When a window receives a ::KMQ_WM_DISPATCH message, it means that + a message has been posted which is of a type that the window has + subscribed for. Because of the way Windows handles window + messages and the way NetIDMgr message queues work, a thread which + has a window (or thread) procedure can not call kmq_dispatch() to + handle these messages. For threads that have window or thread + message loops, they must call kmq_subscribe_hwnd() to subscribe a + particular window (for thread message loops, this would be the + HWND of the message window for the thread) to NetIDMgr messages. + + There are two supported ways of handling the ::KMQ_WM_DISPATCH + message. Examples of both are provided below. + + Handling the message inline: + + \code + LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + kmq_message * m; + khm_int32 rv; + ... + switch(uMsg) { + case WM_CREATE: + ... + kmq_subscribe_hwnd(KMSG_CRED, hwnd); + ... + break; + + case WM_DESTROY: + ... + kmq_unsubscribe_hwnd(KMSG_CRED, hwnd); + ... + break; + + ... + case KMQ_WM_DISPATCH: + kmq_wm_begin(lParam,&m); + + if(m->type == KMSG_CRED && m->subtype == KMSG_CRED_ROOTDELTA) { + // do something + rv = KHM_ERROR_SUCCESS; + } + + return kmq_wm_end(m, rv); + ... + }; + ... + } + \endcode + + The other method is to dispatch the ::KMQ_WM_DISPATCH message to a + secondary callback function: + + \code + khm_int32 msg_handler(khm_int32 t, khm_int32 st, khm_ui_4 up, void * pb) { + khm_int32 rv = KHM_ERROR_SUCCESS; + + //handle message + + return rv; + } + + LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + kmq_message * m; + khm_int32 rv; + ... + switch(uMsg) { + ... + + case WM_CREATE: + ... + kmq_subscribe_hwnd(KMSG_CRED, hwnd); + ... + break; + + case WM_DESTROY: + ... + kmq_unsubscribe_hwnd(KMSG_CRED, hwnd); + ... + break; + + ... + case KMQ_WM_DISPATCH: + return kmq_wm_dispatch(lParam, msg_handler); + ... + }; + ... + } + \endcode + + \note Make sure you unsubscribe from the message type when the + window is destroyed. + + \see kmq_unsubscribe_hwnd() + \see kmq_wm_begin() + \see kmq_wm_end() + \see kmq_wm_dispatch() + */ +KHMEXP khm_int32 KHMAPI kmq_subscribe_hwnd(khm_int32 type, HWND hwnd); + +#ifdef _WIN32 +/*! \brief Begins handling a KMQ_WM_DISPATCH message + + \return The return value of this function should be ignored. + + \see kmq_subscribe_hwnd() for more details about handling ::KMQ_WM_DISPATCH + */ +KHMEXP LRESULT KHMAPI kmq_wm_begin(LPARAM lparm, kmq_message ** m); + +/*! \brief Ends handling a KMQ_WM_DISPATCH message + + \return The return value of this function should be the return + value of the window procedure. See kmq_subscribe_hwnd() + documentation for example + + \see kmq_subscribe_hwnd() for more details about handling ::KMQ_WM_DISPATCH + */ +KHMEXP LRESULT KHMAPI kmq_wm_end(kmq_message *m, khm_int32 rv); + +/*! \brief Dispatches a KMQ_WM_DISPATCH message to a callback + + \return The return value of this function should be the return + value of the window procedure. See kmq_subscribe_hwnd() + documentation for example. + + \see kmq_subscribe_hwnd() for more details about handling ::KMQ_WM_DISPATCH + */ +KHMEXP LRESULT KHMAPI kmq_wm_dispatch(LPARAM lparm, kmq_callback_t cb); +#endif + +/*! \brief Unsubscribe a callback from a message type + + Removes the subscription for message type \a type for callback + function \a cb from the subscription list for the current thread + (the thread that calls kmq_unsubscribe()). + + \note kmq_unsubscribe() can only remove subscriptions for the subscription + list for the current thread. + + \see kmq_subscribe() + \see kmq_dispatch() +*/ +KHMEXP khm_int32 KHMAPI kmq_unsubscribe(khm_int32 type, kmq_callback_t cb); + +/*! \brief Unsubscribe a window from a message type + + Removes the specific window from the subsription list for message + type \a type. + + \see kmq_subscribe_hwnd() +*/ +KHMEXP khm_int32 KHMAPI kmq_unsubscribe_hwnd(khm_int32 type, HWND hwnd); + +/*! \brief Create an ad-hoc subscription + + An ad-hoc subscription describes a callback point in a thread that + can be dispatched messages to individually without broadcasting. + + \see kmq_post_sub_msg(), kmq_post_sub_msg_ex(), + kmq_send_sub_msg(), kmq_post_subs_msg(), + kmq_post_subs_msg_ex(), kmq_send_subs_msg(), + kmq_delete_subscription() +*/ +KHMEXP khm_int32 KHMAPI kmq_create_subscription( + kmq_callback_t cb, + khm_handle * result); + +/*! \brief Delete an ad-hoc subscription + + Deletes a subscriptoin that was created using + kmq_create_subscription() + */ +KHMEXP khm_int32 KHMAPI kmq_delete_subscription(khm_handle sub); + +/*! \brief Post a message to a subscription + + Equivalent of kmq_post_msg() but only posts the message to the + specified subscription. + */ +KHMEXP khm_int32 KHMAPI kmq_post_sub_msg( + khm_handle sub, + khm_int32 type, + khm_int32 subtype, + khm_ui_4 uparam, + void * vparam); + +/*! \brief Post a message to a subscription and acquire a handle to the call + */ +KHMEXP khm_int32 KHMAPI kmq_post_sub_msg_ex( + khm_handle sub, + khm_int32 type, + khm_int32 subtype, + khm_ui_4 uparam, + void * vparam, + kmq_call * call); + +/*! \brief Send a synchronous message to a subscription + + \retval KHM_ERROR_SUCCESS The call succeeded, and no subscribers reported errors + \retval KHM_ERROR_PARTIAL The call succeeded, but at least one subscriber reported errors + */ +KHMEXP khm_int32 KHMAPI kmq_send_sub_msg( + khm_handle sub, + khm_int32 type, + khm_int32 subtype, + khm_ui_4 uparam, + void * vparam); + +/*! \brief Post a message to a group of subscriptions + + The block of memory pointed to by \a subs should be an array of + subscriptions. The number of elements in that array should be \a + n_subs. A message as specified by the remaining parameters will + be dispatched to all of the subscription points in the array. + */ +KHMEXP khm_int32 KHMAPI kmq_post_subs_msg( + khm_handle * subs, + khm_size n_subs, + khm_int32 type, + khm_int32 subtype, + khm_ui_4 uparam, + void * vparam); + +/*! \brief Post a message to a group of subscriptions and acquire a handle to the call + + The block of memory pointed to by \a subs should be an array of + subscriptions. The number of elements in that array should be \a + n_subs. A message as specified by the remaining parameters will + be dispatched to all of the subscription points in the array, and + a handle to the call will be returned in \a call. + + The returned \a call will reference all of the dispatches that + were made. +*/ +KHMEXP khm_int32 KHMAPI kmq_post_subs_msg_ex( + khm_handle * subs, + khm_int32 n_subs, + khm_int32 type, + khm_int32 subtype, + khm_ui_4 uparam, + void * vparam, + kmq_call * call); + +/*! \brief Send a synchronous message to a group of subscriptions + + The block of memory pointed to by \a subs should be an array of + subscriptions. The number of elements in that array should be \a + n_subs. A message as specified by the remaining parameters will + be dispatched to all of the subscription points in the array. The + function will not return until all of the calls have succeeded. + + \retval KHM_ERROR_SUCCESS The call succeeded, and no subscribers reported errors + \retval KHM_ERROR_PARTIAL The call succeeded, but at least one subscriber reported errors +*/ +KHMEXP khm_int32 KHMAPI kmq_send_subs_msg( + khm_handle *subs, + khm_int32 n_subs, + khm_int32 type, + khm_int32 subtype, + khm_ui_4 uparam, + void * vparam); + +/*! \brief Dispatch a message for the current thread. + + This function opens the message list for the current thread and + dispatches the first message instance that is found. Note that if + multiple callbacks subscribe to the same message type in the same + thread, then when a message of that type is received, multiple + message instances are added to the message queue corresponding to + each subscription. + + If no message instances are waiting in the queue, kmq_dispatch() + waits for the \a timeout period for a message. + + \param[in] timeout The timeout period in milliseconds. Specify INFINITE for + kmq_dispatch() to wait indefinitely. + + \retval KHM_ERROR_SUCCESS A message instance was dispatched + \retval KHM_ERROR_TIMEOUT The timeout period elapsed + \retval KHM_ERROR_EXIT The message found on the queue was +*/ +KHMEXP khm_int32 KHMAPI kmq_dispatch(kmq_timer timeout); + +/*! \brief Send a message + + The specified message will be posted to all the subscribers of the + message type. Then the function will wait for all the subscribers + to finish processing the message before returning. + + \param[in] type The type of the message + \param[in] subtype The subtype + \param[in] uparam The khm_ui_4 parameter for the message + \param[in] blob The parameter blob for the message + + \note The internal timeout for this function is INFINITE. If you + it is desirable to use a different timeout, use + kmq_post_message_ex() and kmq_wait() functions. + + \retval KHM_ERROR_SUCCESS The call succeeded and no subscribers returned errors + \retval KHM_ERROR_PARTIAL The call succeeded but at least one subscriber returned an error +*/ +KHMEXP khm_int32 KHMAPI kmq_send_message( + khm_int32 type, + khm_int32 subtype, + khm_ui_4 uparam, + void * blob); + +/*! \brief Post a message + + The specified message will be posted to all the subscribers of the + message type. The function returns immediately. + + If you want to be able to wait for all the subscribers to finish + processing the message, you should use kmq_post_message_ex() + instead. + + \param[in] type The type of the message + \param[in] subtype The subtype + \param[in] uparam The khm_ui_4 parameter for the message + \param[in] blob The parameter blob for the message +*/ +KHMEXP khm_int32 KHMAPI kmq_post_message( + khm_int32 type, + khm_int32 subtype, + khm_ui_4 uparam, + void * blob); + +/*! \brief Post a message and acquire a handle to the call. + + The specified message is posted to all the subscribers. In + addition, a handle is obtained for the call which can be used in + subsequent call to kmq_free_call() or kmq_wait(). + + Call kmq_free_call() to free the handle. + + \param[in] type The type of the message + \param[in] subtype The subtype + \param[in] uparam The khm_ui_4 parameter for the message + \param[in] blob The parameter blob for the message + \param[out] call Receives the call handle. Set to NULL if the call handle is not required. + + \see kmq_free_call() +*/ +KHMEXP khm_int32 KHMAPI kmq_post_message_ex( + khm_int32 type, + khm_int32 subtype, + khm_ui_4 uparam, + void * blob, + kmq_call * call); + +/*! \brief Free a handle to a call obtained through kmq_post_message_ex() + + All call handles obtained through kmq_post_message_ex() must be + freed via a call to kmq_free_call(). +*/ +KHMEXP khm_int32 KHMAPI kmq_free_call(kmq_call call); + +/*! \brief Sends a message to the specified thread. + + The message itself will not be received by any callback function, + however, any kmq_dispatch() function that is currently active of + becomes active will exit with a KHM_ERROR_EXIT code. + kmq_send_thread_quit_message() will wait for this to happen before + returning. + */ +KHMEXP khm_int32 KHMAPI kmq_send_thread_quit_message( + kmq_thread_id thread, + khm_ui_4 uparam); + +/*! \brief Post a message to the specified thread. + + The message itself will not be received by any callback function, + however, any kmq_dispatch() function that is currently active of + becomes active will exit with a KHM_ERROR_EXIT code. + kmq_post_thread_quit_message() will return immediately. + */ +KHMEXP khm_int32 KHMAPI kmq_post_thread_quit_message( + kmq_thread_id thread, + khm_ui_4 uparam, + kmq_call * call); + +KHMEXP khm_int32 KHMAPI kmq_get_next_response(kmq_call call, void ** resp); + +/*! \brief Check if a specific call has completed + + \return TRUE if the call has completed. FALSE otherwise. +*/ +KHMEXP khm_boolean KHMAPI kmq_has_completed(kmq_call call); + +/*! \brief Wait for a call to complete. + + Waits for the specified call to complete. If the call dispatched + to multiple recipients, the function waits for all dispatches to + complete. + + If the call has already completed, then the function returns + immediately. + + If more than one thread is waiting for a single message to + complete, then only one of them will be released when the message + compeltes. Each subsequent thread will be released as each + released thread calls kmq_free_call(). + + \param[in] call A handle to a call. + \param[in] timeout Specifies, in milliseconds, the amount of time + to wait for the call to complete. Specify INFINITE to wait + indefinitely. + + \retval KHM_ERROR_SUCCESS The call completed + \retval KHM_ERROR_TIMEOUT The timeout period expired + \retval KHM_ERROR_INVALID_PARM One of the parameters were invalid. +*/ +KHMEXP khm_int32 KHMAPI kmq_wait(kmq_call call, kmq_timer timeout); + +/*! \brief Sets the completion handler for a specified message type. + + \note Only one completion handler can exist for one message type. + Calling this function overwrites the previous completion + handler. +*/ +KHMEXP khm_int32 KHMAPI kmq_set_completion_handler( + khm_int32 type, + kmq_msg_completion_handler hander); + +/*@}*/ +#endif diff --git a/src/windows/identity/kmq/kmqconfig.csv b/src/windows/identity/kmq/kmqconfig.csv new file mode 100644 index 000000000..c6d5ca451 --- /dev/null +++ b/src/windows/identity/kmq/kmqconfig.csv @@ -0,0 +1,5 @@ +Name,Type,Value,Description +KMQ,KC_SPACE,0,Options for the credentials window + QueueDeadTimeout,KC_INT32,12000, + CallDeadTimeout,KC_INT32,8000, +KMQ,KC_ENDSPACE,0, diff --git a/src/windows/identity/kmq/kmqinternal.h b/src/windows/identity/kmq/kmqinternal.h new file mode 100644 index 000000000..c82fb925d --- /dev/null +++ b/src/windows/identity/kmq/kmqinternal.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KMQINTERNAL_H +#define __KHIMAIRA_KMQINTERNAL_H + +#include +#include +#include +#include +#include +#include +#include + +#define KMQ_CONF_SPACE_NAME L"KMQ" +#define KMQ_CONF_QUEUE_DEAD_TIMEOUT_NAME L"QueueDeadTimeout" +#define KMQ_CONF_CALL_DEAD_TIMEOUT_NAME L"CallDeadTimeout" + +extern CRITICAL_SECTION cs_kmq_global; +extern kmq_timer kmq_queue_dead_timeout; +extern kmq_timer kmq_call_dead_timeout; + +extern kmq_queue * queues; + +/* message type */ +extern CRITICAL_SECTION cs_kmq_types; +extern kmq_msg_type *msg_types[KMQ_MSG_TYPE_MAX+1]; + +void kmqint_init_msg_types(void); +void kmqint_exit_msg_types(void); +void kmqint_free_msg_type(int t); +void kmqint_msg_type_create(int t); +void kmqint_msg_type_add_sub(int t, kmq_msg_subscription *s); +void kmqint_msg_type_del_sub(kmq_msg_subscription *s); +kmq_msg_subscription * kmqint_msg_type_del_sub_hwnd(khm_int32 t, HWND hwnd); +kmq_msg_subscription * kmqint_msg_type_del_sub_cb(khm_int32 t, kmq_callback_t cb); +khm_int32 kmqint_msg_publish(kmq_message * m, khm_boolean try_send); +khm_int32 kmqint_msg_type_set_handler(khm_int32 type, kmq_msg_completion_handler handler); +int kmqint_notify_msg_completion(kmq_message * m); + +/* consumer */ +extern DWORD kmq_tls_queue; + +void kmqint_post_queue(kmq_queue * q, kmq_message *m); +void kmqint_post(kmq_msg_subscription * s, kmq_message * m, khm_boolean try_send); +kmq_queue * kmqint_get_thread_queue(void); +void kmqint_get_queue_message_ref(kmq_queue * q, kmq_message_ref ** r); + +/* publisher */ +extern CRITICAL_SECTION cs_kmq_msg; +extern CRITICAL_SECTION cs_kmq_msg_ref; + +kmq_message * kmqint_get_message(void); +void kmqint_put_message(kmq_message *m); + +void kmqint_init(void); +void kmqint_exit(void); +void kmqint_attach_this_thread(void); +void kmqint_detach_this_thread(void); + +khm_int32 kmqint_post_message_ex( + khm_int32 type, + khm_int32 subtype, + khm_ui_4 uparam, + void * blob, + kmq_call * call, + khm_boolean try_send); + +int kmqint_call_completion_handler(kmq_msg_completion_handler h, + kmq_message * m); + +/* global */ +extern kconf_schema schema_kmqconfig[]; + +/* Lock hiearchy : + + cs_kmq_types + cs_kmq_msg + cs_kmq_msg_ref + cs_compl + cs_kmq_global + kmq_queue::cs + + If you have a level 'x' lock, you can obtain a level 'x+n' lock. + You can't obtain a 'x-n' lock if you already have a level 'x' lock. + If you don't have any locks, you can obtain any lock. + */ +#endif diff --git a/src/windows/identity/kmq/kmqmain.c b/src/windows/identity/kmq/kmqmain.c new file mode 100644 index 000000000..d93403a57 --- /dev/null +++ b/src/windows/identity/kmq/kmqmain.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include + +void +kmq_process_attach(void) { + kmqint_init(); +} + +void +kmq_process_detach(void) { + kmqint_exit(); +} + +void +kmq_thread_attach(void) { + kmqint_attach_this_thread(); +} + +void +kmq_thread_detach(void) { + kmqint_detach_this_thread(); +} diff --git a/src/windows/identity/kmq/msgtype.c b/src/windows/identity/kmq/msgtype.c new file mode 100644 index 000000000..eb44eecf3 --- /dev/null +++ b/src/windows/identity/kmq/msgtype.c @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include + +CRITICAL_SECTION cs_kmq_types; + +kmq_msg_type *msg_types[KMQ_MSG_TYPE_MAX + 1]; +kmq_msg_type *all_msg_types = NULL; + +/*! \internal + \brief Initializes the message type data structures + \note called with cs_mkq_global held */ +void kmqint_init_msg_types(void) { + ZeroMemory(msg_types, sizeof(kmq_msg_type *) * (KMQ_MSG_TYPE_MAX + 1)); + InitializeCriticalSection(&cs_kmq_types); +} + +/*! \internal + \brief Frees up the message type data structures + \note called with cs_mkq_global held */ +void kmqint_exit_msg_types(void) { + int i; + + EnterCriticalSection(&cs_kmq_types); + for(i=0;itype]; + if(mt == NULL) + return 0; + h = mt->completion_handler; + + /* handler is set to NULL before freeing type */ + if(h == NULL || msg_types[m->type] == NULL) + return 0; + + return kmqint_call_completion_handler(h,m); +} + +/* called with cs_mkq_global && cs_kmq_types held */ +void kmqint_free_msg_type(int t) { + /*TODO: free the message type*/ + /* must set handler to NULL before freeing type */ + /* must set msg_type[t] = NULL before starting to free type */ +} + +/*! \internal + \brief Create a message type + \note Obtains ::cs_kmq_types + */ +void kmqint_msg_type_create(int t) { + if(t < 0 || t > KMQ_MSG_TYPE_MAX) + return; + + EnterCriticalSection(&cs_kmq_types); + if(!msg_types[t]) { + kmq_msg_type * mt; + mt = malloc(sizeof(kmq_msg_type)); + ZeroMemory(mt, sizeof(kmq_msg_type)); + mt->id = t; + LINIT(mt); + mt->subs = NULL; + msg_types[t] = mt; + + LPUSH(&all_msg_types, mt); + } + LeaveCriticalSection(&cs_kmq_types); +} + +KHMEXP khm_int32 KHMAPI kmq_register_type(wchar_t * name, + khm_int32 * new_id) +{ + int i; + khm_int32 rv = KHM_ERROR_SUCCESS; + BOOL registered = FALSE; + int first_free = 0; + size_t sz; + + if(FAILED(StringCbLength(name, KMQ_MAXCB_TYPE_NAME, &sz)) || + sz == 0) + return KHM_ERROR_INVALID_PARM; + sz += sizeof(wchar_t); + + EnterCriticalSection(&cs_kmq_types); + for(i=KMSGBASE_USER; i <= KMQ_MSG_TYPE_MAX; i++) { + if(msg_types[i] == NULL) { + if(first_free == 0) + first_free = i; + } else { + if(msg_types[i]->name != NULL && + !wcscmp(msg_types[i]->name, name)) + registered = TRUE; + } + } + + if(registered) { + rv = KHM_ERROR_EXISTS; + } else if(first_free == 0) { + rv = KHM_ERROR_NO_RESOURCES; + } else { + kmqint_msg_type_create(first_free); + msg_types[first_free]->name = malloc(sz); + StringCbCopy(msg_types[first_free]->name, sz, name); + + if(new_id != NULL) + *new_id = first_free; + } + LeaveCriticalSection(&cs_kmq_types); + + return rv; +} + +KHMEXP khm_int32 KHMAPI kmq_find_type(wchar_t * name, khm_int32 * id) +{ + int i; + + EnterCriticalSection(&cs_kmq_types); + for(i=KMSGBASE_USER; i <= KMQ_MSG_TYPE_MAX; i++) { + if(msg_types[i] != NULL && msg_types[i]->name != NULL) { + if(!wcscmp(msg_types[i]->name, name)) + break; + } + } + LeaveCriticalSection(&cs_kmq_types); + + if(i <= KMQ_MSG_TYPE_MAX) { + if(id != NULL) + *id = i; + return KHM_ERROR_SUCCESS; + } + + return KHM_ERROR_NOT_FOUND; + +} + +KHMEXP khm_int32 KHMAPI kmq_unregister_type(khm_int32 id) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(id < KMSGBASE_USER || id > KMQ_MSG_TYPE_MAX) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_kmq_types); + if(msg_types[id] != NULL) { + EnterCriticalSection(&cs_kmq_global); + kmqint_free_msg_type(id); + LeaveCriticalSection(&cs_kmq_global); + } else { + rv = KHM_ERROR_NOT_FOUND; + } + LeaveCriticalSection(&cs_kmq_types); + + return rv; +} + +/*! \internal + \brief Adds a subscription to a message type + \note Obtains ::cs_kmq_types + */ +void kmqint_msg_type_add_sub(int t, kmq_msg_subscription *s) { + kmq_msg_subscription * ts; + + if(t < 0 || t > KMQ_MSG_TYPE_MAX) + return; + + if(!msg_types[t]) + kmqint_msg_type_create(t); + + EnterCriticalSection(&cs_kmq_types); + s->type = t; + /* check if we already have this subscription */ + ts = msg_types[t]->subs; + while(ts) { + if((ts->rcpt_type == s->rcpt_type) && + (((ts->rcpt_type == KMQ_RCPTTYPE_CB) && (ts->recipient.cb == s->recipient.cb)) || + ((ts->rcpt_type == KMQ_RCPTTYPE_HWND) && (ts->recipient.hwnd == s->recipient.hwnd)))) + break; + ts = LNEXT(ts); + } + /* add it if we didn't find it */ + if(!ts) { + LPUSH(&msg_types[t]->subs, s); + } + LeaveCriticalSection(&cs_kmq_types); +} + +/*! \internal + \brief Delete a subscription + \note Obtains ::cs_kmq_types + */ +void kmqint_msg_type_del_sub(kmq_msg_subscription *s) { + int t = s->type; + + EnterCriticalSection(&cs_kmq_types); + if(msg_types[t]) { + LDELETE(&msg_types[t]->subs,s); + } + LeaveCriticalSection(&cs_kmq_types); +} + +/*! \internal + \brief Deletes a window subscription from a message type + \note Obtains ::cs_kmq_types +*/ +kmq_msg_subscription * kmqint_msg_type_del_sub_hwnd(khm_int32 t, HWND hwnd) { + kmq_msg_subscription *s; + + if(t < 0 || t > KMQ_MSG_TYPE_MAX) + return NULL; + + EnterCriticalSection(&cs_kmq_types); + if(msg_types[t]) { + s = msg_types[t]->subs; + while(s) { + kmq_msg_subscription * n = LNEXT(s); + if(s->rcpt_type == KMQ_RCPTTYPE_HWND && s->recipient.hwnd == hwnd) { + /*TODO: do more here? */ + LDELETE(&msg_types[t]->subs, s); + break; + } + s = n; + } + } + LeaveCriticalSection(&cs_kmq_types); + + return s; +} + +/*! \internal + \brief Delete a callback from a message type + \note Obtains ::cs_kmq_types, ::cs_kmq_global + */ +kmq_msg_subscription * kmqint_msg_type_del_sub_cb(khm_int32 t, kmq_callback_t cb) { + kmq_msg_subscription *s; + kmq_queue *q; + + if(t < 0 || t > KMQ_MSG_TYPE_MAX) + return NULL; + + if(!msg_types[t]) + return NULL; + + q = kmqint_get_thread_queue(); + + EnterCriticalSection(&cs_kmq_types); + s = msg_types[t]->subs; + while(s) { + kmq_msg_subscription * n = LNEXT(s); + if(s->rcpt_type == KMQ_RCPTTYPE_CB && s->recipient.cb == cb && s->queue == q) { + /*TODO: do more here? */ + LDELETE(&msg_types[t]->subs, s); + break; + } + s = n; + } + LeaveCriticalSection(&cs_kmq_types); + + return s; +} + +/*! \internal + \brief Publish a message + \note Obtains ::cs_kmq_types, ::cs_kmq_msg_ref, kmq_queue::cs, ::cs_kmq_msg + */ +khm_int32 kmqint_msg_publish(kmq_message * m, khm_boolean try_send) { + khm_int32 rv = KHM_ERROR_SUCCESS; + if(msg_types[m->type]) { + kmq_msg_type *t; + kmq_msg_subscription * s; + + EnterCriticalSection(&cs_kmq_types); + EnterCriticalSection(&cs_kmq_msg); + t = msg_types[m->type]; + s = t->subs; + while(s) { + kmqint_post(s, m, try_send); + s = LNEXT(s); + } + + if(m->nCompleted + m->nFailed == m->nSent) { + kmqint_put_message(m); + } + + LeaveCriticalSection(&cs_kmq_msg); + LeaveCriticalSection(&cs_kmq_types); + + } else { + EnterCriticalSection(&cs_kmq_msg); + kmqint_put_message(m); + LeaveCriticalSection(&cs_kmq_msg); + } + return rv; +} + +/*! \internal + \brief Sets the completion handler for a message type + \note Obtains ::cs_kmq_types + */ +khm_int32 kmqint_msg_type_set_handler(khm_int32 type, kmq_msg_completion_handler handler) { + + if (type == KMSG_SYSTEM) + return KHM_ERROR_INVALID_PARM; + + if(!msg_types[type]) + kmqint_msg_type_create(type); + + if(!msg_types[type]) + return KHM_ERROR_NO_RESOURCES; + + EnterCriticalSection(&cs_kmq_types); + msg_types[type]->completion_handler = handler; + LeaveCriticalSection(&cs_kmq_types); + + return KHM_ERROR_SUCCESS; +} diff --git a/src/windows/identity/kmq/publisher.c b/src/windows/identity/kmq/publisher.c new file mode 100644 index 000000000..5391844ac --- /dev/null +++ b/src/windows/identity/kmq/publisher.c @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include + +CRITICAL_SECTION cs_kmq_msg; +kmq_message * msg_free = NULL; +kmq_message * msg_active = NULL; + +/*! \internal + \brief Get a message object + \note called with ::cs_kmq_msg held */ +kmq_message * kmqint_get_message(void) { + kmq_message * m; + + LPOP(&msg_free,&m); + if(!m) { + /* allocate one */ + m = malloc(sizeof(kmq_message)); + } + ZeroMemory((void*)m, sizeof(kmq_message)); + + LPUSH(&msg_active, m); + + return m; +} + +/*! \internal + \brief Frees a message object + \note called with ::cs_kmq_msg held + */ +void kmqint_put_message(kmq_message *m) { + int queued; + /* we can only free a message if the refcount is zero. + Otherwise we have to wait until the call is freed. */ + if(m->refcount == 0) { + LDELETE(&msg_active, m); + LeaveCriticalSection(&cs_kmq_msg); + queued = kmqint_notify_msg_completion(m); + EnterCriticalSection(&cs_kmq_msg); + if (!queued) { + if(m->err_ctx) { + kherr_release_context(m->err_ctx); + m->err_ctx = NULL; + } + if(m->wait_o) { + CloseHandle(m->wait_o); + m->wait_o = NULL; + } + LPUSH(&msg_free,m); + } + } else if(m->wait_o) { + SetEvent(m->wait_o); + } +} + +/*! \internal + \note Obtains ::cs_kmq_msg, ::cs_kmq_types, ::cs_kmq_msg_ref, kmq_queue::cs + */ +KHMEXP khm_int32 KHMAPI kmq_send_message(khm_int32 type, khm_int32 subtype, khm_ui_4 uparam, void * blob) { + kmq_call c; + khm_int32 rv = KHM_ERROR_SUCCESS; + + rv = kmqint_post_message_ex(type, subtype, uparam, blob, &c, TRUE); + if(KHM_FAILED(rv)) + return rv; + + rv = kmq_wait(c, INFINITE); + if(KHM_SUCCEEDED(rv) && c->nFailed > 0) + rv = KHM_ERROR_PARTIAL; + + kmq_free_call(c); + + return rv; +} + +/*! \internal + \note Obtains ::cs_kmq_msg, ::cs_kmq_types, ::cs_kmq_msg_ref, kmq_queue::cs + */ +KHMEXP khm_int32 KHMAPI kmq_post_message(khm_int32 type, khm_int32 subtype, khm_ui_4 uparam, void * blob) { + return kmqint_post_message_ex(type, subtype, uparam, blob, NULL, FALSE); +} + +/*! \internal + \brief Frees a call + \note Obtains ::cs_kmq_msg + */ +KHMEXP khm_int32 KHMAPI kmq_free_call(kmq_call call) { + kmq_message * m; + + m = call; + + EnterCriticalSection(&cs_kmq_msg); + m->refcount--; + if(!m->refcount) { + kmqint_put_message(m); + } + LeaveCriticalSection(&cs_kmq_msg); + + return KHM_ERROR_SUCCESS; +} + +/*! \internal + \note Obtains ::cs_kmq_msg, ::cs_kmq_types, ::cs_kmq_msg_ref, kmq_queue::cs + */ +khm_int32 kmqint_post_message_ex( + khm_int32 type, + khm_int32 subtype, + khm_ui_4 uparam, + void * blob, + kmq_call * call, + khm_boolean try_send) { + kmq_message * m; + kherr_context * ctx; + + EnterCriticalSection(&cs_kmq_msg); + m = kmqint_get_message(); + LeaveCriticalSection(&cs_kmq_msg); + + m->type = type; + m->subtype = subtype; + m->uparam = uparam; + m->vparam = blob; + + m->timeSent = GetTickCount(); + m->timeExpire = m->timeSent + kmq_call_dead_timeout; + + ctx = kherr_peek_context(); + if (ctx) { + if (ctx->flags & KHERR_CF_TRANSITIVE) { + m->err_ctx = ctx; + /* leave it held */ + } else { + kherr_release_context(ctx); + } + } + + if(call) { + m->wait_o = CreateEvent(NULL,FALSE,FALSE,NULL); + *call = m; + m->refcount++; + } else + m->wait_o = NULL; + + kmqint_msg_publish(m, try_send); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kmq_post_message_ex(khm_int32 type, khm_int32 subtype, khm_ui_4 uparam, void * blob, kmq_call * call) +{ + return kmqint_post_message_ex(type, subtype, uparam, blob, call, FALSE); +} + + +/*! \internal +*/ +KHMEXP khm_int32 KHMAPI kmq_post_sub_msg(khm_handle sub, khm_int32 type, khm_int32 subtype, khm_ui_4 uparam, void * vparam) +{ + return kmq_post_sub_msg_ex(sub, type, subtype, uparam, vparam, NULL); +} + +/*! \internal +*/ +khm_int32 kmqint_post_sub_msg_ex( + khm_handle sub, + khm_int32 type, + khm_int32 subtype, + khm_ui_4 uparam, + void * vparam, + kmq_call * call, + khm_boolean try_send) +{ + kmq_message * m; + kherr_context * ctx; + + EnterCriticalSection(&cs_kmq_msg); + m = kmqint_get_message(); + LeaveCriticalSection(&cs_kmq_msg); + + m->type = type; + m->subtype = subtype; + m->uparam = uparam; + m->vparam = vparam; + + m->timeSent = GetTickCount(); + m->timeExpire = m->timeSent + kmq_call_dead_timeout; + + ctx = kherr_peek_context(); + if (ctx) { + if (ctx->flags & KHERR_CF_TRANSITIVE) { + m->err_ctx = ctx; + /* leave it held */ + } else { + kherr_release_context(ctx); + } + } + + if(call) { + m->wait_o = CreateEvent(NULL,FALSE,FALSE,NULL); + *call = m; + m->refcount++; + } else + m->wait_o = NULL; + + EnterCriticalSection(&cs_kmq_msg); + kmqint_post((kmq_msg_subscription *) sub, m, try_send); + + if(m->nCompleted + m->nFailed == m->nSent) { + kmqint_put_message(m); + } + LeaveCriticalSection(&cs_kmq_msg); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kmq_post_sub_msg_ex(khm_handle sub, khm_int32 type, khm_int32 subtype, khm_ui_4 uparam, void * vparam, kmq_call * call) +{ + return kmqint_post_sub_msg_ex(sub, type, subtype, uparam, vparam, call, FALSE); +} + +khm_int32 kmqint_post_subs_msg_ex( + khm_handle * subs, + khm_size n_subs, + khm_int32 type, + khm_int32 subtype, + khm_ui_4 uparam, + void * vparam, + kmq_call * call, + khm_boolean try_send) +{ + kmq_message * m; + kherr_context * ctx; + khm_size i; + + if(n_subs == 0) + return KHM_ERROR_SUCCESS; + + EnterCriticalSection(&cs_kmq_msg); + m = kmqint_get_message(); + LeaveCriticalSection(&cs_kmq_msg); + + m->type = type; + m->subtype = subtype; + m->uparam = uparam; + m->vparam = vparam; + + m->timeSent = GetTickCount(); + m->timeExpire = m->timeSent + kmq_call_dead_timeout; + + ctx = kherr_peek_context(); + if (ctx) { + if (ctx->flags & KHERR_CF_TRANSITIVE) { + m->err_ctx = ctx; + /* leave it held */ + } else { + kherr_release_context(ctx); + } + } + + if(call) { + m->wait_o = CreateEvent(NULL,FALSE,FALSE,NULL); + *call = m; + m->refcount++; + } else + m->wait_o = NULL; + + EnterCriticalSection(&cs_kmq_msg); + for(i=0;inCompleted + m->nFailed == m->nSent) { + kmqint_put_message(m); + } + LeaveCriticalSection(&cs_kmq_msg); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI kmq_post_subs_msg( + khm_handle * subs, + khm_size n_subs, + khm_int32 type, + khm_int32 subtype, + khm_ui_4 uparam, + void * vparam) +{ + return kmqint_post_subs_msg_ex( + subs, + n_subs, + type, + subtype, + uparam, + vparam, + NULL, + FALSE); +} + +KHMEXP khm_int32 KHMAPI kmq_post_subs_msg_ex( + khm_handle * subs, + khm_int32 n_subs, + khm_int32 type, + khm_int32 subtype, + khm_ui_4 uparam, + void * vparam, + kmq_call * call) +{ + return kmqint_post_subs_msg_ex(subs, n_subs, type, subtype, uparam, vparam, call, FALSE); +} + +KHMEXP khm_int32 KHMAPI kmq_send_subs_msg( + khm_handle *subs, + khm_int32 n_subs, + khm_int32 type, + khm_int32 subtype, + khm_ui_4 uparam, + void * vparam) +{ + kmq_call c; + khm_int32 rv = KHM_ERROR_SUCCESS; + + rv = kmqint_post_subs_msg_ex(subs, n_subs, type, subtype, uparam, vparam, &c, TRUE); + if(KHM_FAILED(rv)) + return rv; + + rv = kmq_wait(c, INFINITE); + if(KHM_SUCCEEDED(rv) && c->nFailed > 0) + rv = KHM_ERROR_PARTIAL; + + kmq_free_call(c); + + return rv; +} + +/*! \internal +*/ +KHMEXP khm_int32 KHMAPI kmq_send_sub_msg(khm_handle sub, khm_int32 type, khm_int32 subtype, khm_ui_4 uparam, void * vparam) +{ + kmq_call c; + khm_int32 rv = KHM_ERROR_SUCCESS; + + rv = kmqint_post_sub_msg_ex(sub, type, subtype, uparam, vparam, &c, TRUE); + if(KHM_FAILED(rv)) + return rv; + + rv = kmq_wait(c, INFINITE); + if(KHM_SUCCEEDED(rv) && c->nFailed > 0) + rv = KHM_ERROR_PARTIAL; + + kmq_free_call(c); + + return rv; +} + +/*! \internal + \note Obtains ::cs_kmq_global, ::cs_kmq_msg, ::cs_kmq_msg_ref, kmq_queue::cs + */ +KHMEXP khm_int32 KHMAPI kmq_send_thread_quit_message(kmq_thread_id thread, khm_ui_4 uparam) { + kmq_call c; + khm_int32 rv = KHM_ERROR_SUCCESS; + + rv = kmq_post_thread_quit_message(thread, uparam, &c); + if(KHM_FAILED(rv)) + return rv; + + rv = kmq_wait(c, INFINITE); + + kmq_free_call(c); + + return rv; +} + +/*! \internal + \note Obtains ::cs_kmq_global, ::cs_kmq_msg, ::cs_kmq_msg_ref, kmq_queue::cs + */ +KHMEXP khm_int32 KHMAPI kmq_post_thread_quit_message(kmq_thread_id thread, khm_ui_4 uparam, kmq_call * call) { + kmq_message * m; + kmq_queue * q; + + EnterCriticalSection(&cs_kmq_global); + q = queues; + while(q) { + if(q->thread == thread) + break; + q = LNEXT(q); + } + LeaveCriticalSection(&cs_kmq_global); + + if(!q) + return KHM_ERROR_NOT_FOUND; + + EnterCriticalSection(&cs_kmq_msg); + m = kmqint_get_message(); + LeaveCriticalSection(&cs_kmq_msg); + + m->type = KMSG_SYSTEM; + m->subtype = KMSG_SYSTEM_EXIT; + m->uparam = uparam; + m->vparam = NULL; + + m->timeSent = GetTickCount(); + m->timeExpire = m->timeSent + kmq_call_dead_timeout; + + if(call) { + m->wait_o = CreateEvent(NULL,FALSE,FALSE,NULL); + *call = m; + m->refcount++; + } else + m->wait_o = NULL; + + kmqint_post_queue(q, m); + + return KHM_ERROR_SUCCESS; +} + +/* TODO:Implement these */ +KHMEXP khm_int32 KHMAPI kmq_get_next_response(kmq_call call, void ** resp) { + return 0; +} + +KHMEXP khm_boolean KHMAPI kmq_has_completed(kmq_call call) { + return (call->nCompleted + call->nFailed == call->nSent); +} + +KHMEXP khm_int32 KHMAPI kmq_wait(kmq_call call, kmq_timer timeout) { + kmq_message * m = call; + DWORD rv; + /*TODO: check for call free */ + + if(m && m->wait_o) { + rv = WaitForSingleObject(m->wait_o, timeout); + if(rv == WAIT_OBJECT_0) + return KHM_ERROR_SUCCESS; + else + return KHM_ERROR_TIMEOUT; + } else + return KHM_ERROR_INVALID_PARM; +} + +/*! \internal + \note Obtains ::cs_kmq_types + */ +KHMEXP khm_int32 KHMAPI kmq_set_completion_handler(khm_int32 type, kmq_msg_completion_handler handler) { + return kmqint_msg_type_set_handler(type, handler); +} + diff --git a/src/windows/identity/nidmgrdll/Makefile b/src/windows/identity/nidmgrdll/Makefile new file mode 100644 index 000000000..5d48c2d12 --- /dev/null +++ b/src/windows/identity/nidmgrdll/Makefile @@ -0,0 +1,106 @@ +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +MODULE=nidmgrdll +!include <../config/Makefile.w32> + +DLLFILE=$(BINDIR)\nidmgr32.dll + +LIBFILE=$(LIBDIR)\nidmgr32.lib + +UTILDIR=$(OBJDIR)\util + +KHERRDIR=$(OBJDIR)\kherr + +KCONFIGDIR=$(OBJDIR)\kconfig + +KMQDIR=$(OBJDIR)\kmq + +KCDBDIR=$(OBJDIR)\kcreddb + +KMMDIR=$(OBJDIR)\kmm + +UIDIR=$(OBJDIR)\uilib + +OBJFILES= \ + $(OBJ)\dllmain.obj \ + $(UTILDIR)\hashtable.obj \ + $(UTILDIR)\sync.obj \ + $(UTILDIR)\mstring.obj \ + $(KHERRDIR)\kherrmain.obj \ + $(KHERRDIR)\kherr.obj \ + $(KCONFIGDIR)\kconfigmain.obj \ + $(KCONFIGDIR)\api.obj \ + $(KMQDIR)\kmqmain.obj \ + $(KMQDIR)\init.obj \ + $(KMQDIR)\msgtype.obj \ + $(KMQDIR)\consumer.obj \ + $(KMQDIR)\publisher.obj \ + $(KMQDIR)\kmqconfig.obj \ + $(KCDBDIR)\buf.obj \ + $(KCDBDIR)\attrib.obj \ + $(KCDBDIR)\credential.obj \ + $(KCDBDIR)\credset.obj \ + $(KCDBDIR)\credtype.obj \ + $(KCDBDIR)\identity.obj \ + $(KCDBDIR)\init.obj \ + $(KCDBDIR)\kcreddbmain.obj \ + $(KCDBDIR)\type.obj \ + $(KCDBDIR)\kcdbconfig.obj \ + $(KMMDIR)\kmmmain.obj \ + $(KMMDIR)\kmm.obj \ + $(KMMDIR)\kmm_plugin.obj \ + $(KMMDIR)\kmm_module.obj \ + $(KMMDIR)\kmm_reg.obj \ + $(KMMDIR)\kmm_registrar.obj \ + $(KMMDIR)\kmmconfig.obj \ + $(UIDIR)\rescache.obj \ + $(UIDIR)\action.obj \ + $(UIDIR)\creddlg.obj \ + $(UIDIR)\alert.obj \ + $(UIDIR)\propsheet.obj \ + $(UIDIR)\propwnd.obj \ + $(UIDIR)\uilibmain.obj \ + $(UIDIR)\actiondef.obj \ + $(UIDIR)\acceldef.obj \ + $(UIDIR)\configui.obj \ + $(UIDIR)\trackerwnd.obj + +RESFILES= \ + $(OBJ)\nidmgrdll.res \ + $(KCDBDIR)\kcredres.res \ + $(KMMDIR)\kmm_msgs.res \ + +SDKLIBFILES= \ + advapi32.lib \ + strsafe.lib \ + comctl32.lib + +$(DLLFILE): $(OBJFILES) $(RESFILES) + $(DLLGUILINK) $(LIBFILES) $(SDKLIBFILES) + +all: mkdirs $(DLLFILE) + +clean:: + $(RM) $(DLLFILE) diff --git a/src/windows/identity/nidmgrdll/dllmain.c b/src/windows/identity/nidmgrdll/dllmain.c new file mode 100644 index 000000000..f54d4ef72 --- /dev/null +++ b/src/windows/identity/nidmgrdll/dllmain.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include + +/* forward dcls */ +void +kherr_process_attach(void); + +void +kherr_process_detach(void); + +void +kherr_thread_attach(void); + +void +kherr_thread_detach(void); + +void +kconfig_process_attach(void); + +void +kconfig_process_detach(void); + +void +kmq_process_attach(void); + +void +kmq_process_detach(void); + +void +kmq_thread_attach(void); + +void +kmq_thread_detach(void); + +void +kcdb_process_attach(HINSTANCE); + +void +kcdb_process_detach(void); + +void +kmm_process_attach(HINSTANCE); + +void +kmm_process_detach(void); + +void +uilib_process_attach(void); + +void +uilib_process_detach(void); + + +BOOL WINAPI DllMain( + HINSTANCE hinstDLL, // handle to DLL module + DWORD fdwReason, // reason for calling function + LPVOID lpReserved ) // reserved +{ + switch(fdwReason) { + case DLL_PROCESS_ATTACH: + kherr_process_attach(); + kconfig_process_attach(); + kmq_process_attach(); + kcdb_process_attach(hinstDLL); + kmm_process_attach(hinstDLL); + uilib_process_attach(); + break; + + case DLL_PROCESS_DETACH: + kherr_process_detach(); + kconfig_process_detach(); + kmq_process_detach(); + kcdb_process_detach(); + kmm_process_detach(); + uilib_process_detach(); + break; + + case DLL_THREAD_ATTACH: + kherr_thread_attach(); + kmq_thread_attach(); + break; + + case DLL_THREAD_DETACH: + kherr_thread_detach(); + kmq_thread_detach(); + break; + } + return TRUE; +} diff --git a/src/windows/identity/nidmgrdll/nidmgrdll.rc b/src/windows/identity/nidmgrdll/nidmgrdll.rc new file mode 100644 index 000000000..10870e8c3 --- /dev/null +++ b/src/windows/identity/nidmgrdll/nidmgrdll.rc @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include + +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif + +1 VERSIONINFO + FILEVERSION KH_VERSION_LIST + PRODUCTVERSION KH_VERSION_LIST + FILEFLAGSMASK 0x17L + FILEFLAGS KH_VER_FILEFLAGS + FILEOS KH_VER_FILEOS + FILETYPE KH_VER_FILETYPEDLL + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", KH_VERSTR_COMPANY_1033 + VALUE "FileDescription", "NetIDMgr API" + VALUE "FileVersion", KH_VERSION_STRING + VALUE "InternalName", "nidmgr32" + VALUE "LegalCopyright", KH_VERSTR_COPYRIGHT_1033 + VALUE "OriginalFilename", "nidmgr32.dll" + VALUE "ProductName", KH_VERSTR_PRODUCT_1033 + VALUE "ProductVersion", KH_VERSTR_VERSION_1033 +#ifdef KH_VERSTR_COMMENT_1033 + VALUE "Comments", KH_VERSTR_COMMENT_1033 +#endif +#ifdef KH_VERSTR_PRIVATE_1033 + VALUE "PrivateBuild", KH_VERSTR_PRIVATE_1033 +#endif +#ifdef KH_VERSTR_SPECIAL_1033 + VALUE "SpecialBuild", KH_VERSTR_SPECIAL_1033 +#endif + END + END + +/* Language independent */ + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END + +END diff --git a/src/windows/identity/plugins/common/Makefile b/src/windows/identity/plugins/common/Makefile new file mode 100644 index 000000000..cbadbc644 --- /dev/null +++ b/src/windows/identity/plugins/common/Makefile @@ -0,0 +1,42 @@ +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +MODULE=plugins\common +!include <../../config/Makefile.w32> + +INCFILES= \ + $(INCDIR)\krb5common.h \ + $(INCDIR)\dynimport.h + +OBJFILES= \ + $(LIBDIR)\krb5common.obj \ + $(LIBDIR)\dynimport.obj + +all: mkdirs $(INCFILES) $(OBJFILES) + +clean:: + $(RM) $(INCFILES) + +{}.c{$(LIBDIR)}.obj: + $(C2OBJ) diff --git a/src/windows/identity/plugins/common/dynimport.c b/src/windows/identity/plugins/common/dynimport.c new file mode 100644 index 000000000..cd33813f7 --- /dev/null +++ b/src/windows/identity/plugins/common/dynimport.c @@ -0,0 +1,420 @@ +/* +* Copyright (c) 2004 Massachusetts Institute of Technology +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, +* modify, merge, publish, distribute, sublicense, and/or sell copies +* of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +/* $Id$ */ + +#include +#include +#include +#include + +HINSTANCE hKrb4 = 0; +HINSTANCE hKrb5 = 0; +HINSTANCE hKrb524 = 0; +HINSTANCE hSecur32 = 0; +HINSTANCE hComErr = 0; +HINSTANCE hService = 0; +HINSTANCE hProfile = 0; +HINSTANCE hPsapi = 0; +HINSTANCE hToolHelp32 = 0; +HINSTANCE hCCAPI = 0; + +DWORD AfsAvailable = 0; + +// CCAPI +DECL_FUNC_PTR(cc_initialize); +DECL_FUNC_PTR(cc_shutdown); +DECL_FUNC_PTR(cc_get_NC_info); +DECL_FUNC_PTR(cc_free_NC_info); + +// krb4 functions +DECL_FUNC_PTR(get_krb_err_txt_entry); +DECL_FUNC_PTR(k_isinst); +DECL_FUNC_PTR(k_isname); +DECL_FUNC_PTR(k_isrealm); +DECL_FUNC_PTR(kadm_change_your_password); +DECL_FUNC_PTR(kname_parse); +DECL_FUNC_PTR(krb_get_cred); +DECL_FUNC_PTR(krb_get_krbhst); +DECL_FUNC_PTR(krb_get_lrealm); +DECL_FUNC_PTR(krb_get_pw_in_tkt); +DECL_FUNC_PTR(krb_get_tf_realm); +DECL_FUNC_PTR(krb_mk_req); +DECL_FUNC_PTR(krb_realmofhost); +DECL_FUNC_PTR(tf_init); +DECL_FUNC_PTR(tf_close); +DECL_FUNC_PTR(tf_get_cred); +DECL_FUNC_PTR(tf_get_pname); +DECL_FUNC_PTR(tf_get_pinst); +DECL_FUNC_PTR(LocalHostAddr); +DECL_FUNC_PTR(tkt_string); +DECL_FUNC_PTR(krb_set_tkt_string); +DECL_FUNC_PTR(initialize_krb_error_func); +DECL_FUNC_PTR(initialize_kadm_error_table); +DECL_FUNC_PTR(dest_tkt); +DECL_FUNC_PTR(krb_in_tkt); +DECL_FUNC_PTR(krb_save_credentials); +DECL_FUNC_PTR(krb_get_krbconf2); +DECL_FUNC_PTR(krb_get_krbrealm2); +DECL_FUNC_PTR(krb_life_to_time); + +// krb5 functions +DECL_FUNC_PTR(krb5_change_password); +DECL_FUNC_PTR(krb5_get_init_creds_opt_init); +DECL_FUNC_PTR(krb5_get_init_creds_opt_set_tkt_life); +DECL_FUNC_PTR(krb5_get_init_creds_opt_set_renew_life); +DECL_FUNC_PTR(krb5_get_init_creds_opt_set_forwardable); +DECL_FUNC_PTR(krb5_get_init_creds_opt_set_proxiable); +DECL_FUNC_PTR(krb5_get_init_creds_opt_set_address_list); +DECL_FUNC_PTR(krb5_get_init_creds_password); +DECL_FUNC_PTR(krb5_get_prompt_types); +DECL_FUNC_PTR(krb5_build_principal_ext); +DECL_FUNC_PTR(krb5_cc_get_name); +DECL_FUNC_PTR(krb5_cc_resolve); +DECL_FUNC_PTR(krb5_cc_default); +DECL_FUNC_PTR(krb5_cc_default_name); +DECL_FUNC_PTR(krb5_cc_set_default_name); +DECL_FUNC_PTR(krb5_cc_initialize); +DECL_FUNC_PTR(krb5_cc_destroy); +DECL_FUNC_PTR(krb5_cc_close); +DECL_FUNC_PTR(krb5_cc_store_cred); +DECL_FUNC_PTR(krb5_cc_copy_creds); +DECL_FUNC_PTR(krb5_cc_retrieve_cred); +DECL_FUNC_PTR(krb5_cc_get_principal); +DECL_FUNC_PTR(krb5_cc_start_seq_get); +DECL_FUNC_PTR(krb5_cc_next_cred); +DECL_FUNC_PTR(krb5_cc_end_seq_get); +DECL_FUNC_PTR(krb5_cc_remove_cred); +DECL_FUNC_PTR(krb5_cc_set_flags); +// DECL_FUNC_PTR(krb5_cc_get_type); +DECL_FUNC_PTR(krb5_free_context); +DECL_FUNC_PTR(krb5_free_cred_contents); +DECL_FUNC_PTR(krb5_free_principal); +DECL_FUNC_PTR(krb5_get_in_tkt_with_password); +DECL_FUNC_PTR(krb5_init_context); +DECL_FUNC_PTR(krb5_parse_name); +DECL_FUNC_PTR(krb5_timeofday); +DECL_FUNC_PTR(krb5_timestamp_to_sfstring); +DECL_FUNC_PTR(krb5_unparse_name); +DECL_FUNC_PTR(krb5_get_credentials); +DECL_FUNC_PTR(krb5_mk_req); +DECL_FUNC_PTR(krb5_sname_to_principal); +DECL_FUNC_PTR(krb5_get_credentials_renew); +DECL_FUNC_PTR(krb5_free_data); +DECL_FUNC_PTR(krb5_free_data_contents); +// DECL_FUNC_PTR(krb5_get_realm_domain); +DECL_FUNC_PTR(krb5_free_unparsed_name); +DECL_FUNC_PTR(krb5_os_localaddr); +DECL_FUNC_PTR(krb5_copy_keyblock_contents); +DECL_FUNC_PTR(krb5_copy_data); +DECL_FUNC_PTR(krb5_free_creds); +DECL_FUNC_PTR(krb5_build_principal); +DECL_FUNC_PTR(krb5_get_renewed_creds); +DECL_FUNC_PTR(krb5_get_default_config_files); +DECL_FUNC_PTR(krb5_free_config_files); +DECL_FUNC_PTR(krb5_get_default_realm); +DECL_FUNC_PTR(krb5_free_ticket); +DECL_FUNC_PTR(krb5_decode_ticket); +DECL_FUNC_PTR(krb5_get_host_realm); +DECL_FUNC_PTR(krb5_free_host_realm); +DECL_FUNC_PTR(krb5_c_random_make_octets); +DECL_FUNC_PTR(krb5_free_addresses); +DECL_FUNC_PTR(krb5_free_default_realm); + +// Krb524 functions +DECL_FUNC_PTR(krb524_init_ets); +DECL_FUNC_PTR(krb524_convert_creds_kdc); + +// ComErr functions +DECL_FUNC_PTR(com_err); +DECL_FUNC_PTR(error_message); + +// Profile functions +DECL_FUNC_PTR(profile_init); +DECL_FUNC_PTR(profile_release); +DECL_FUNC_PTR(profile_get_subsection_names); +DECL_FUNC_PTR(profile_free_list); +DECL_FUNC_PTR(profile_get_string); +DECL_FUNC_PTR(profile_release_string); + +// Service functions +DECL_FUNC_PTR(OpenSCManagerA); +DECL_FUNC_PTR(OpenServiceA); +DECL_FUNC_PTR(QueryServiceStatus); +DECL_FUNC_PTR(CloseServiceHandle); +DECL_FUNC_PTR(LsaNtStatusToWinError); + +// LSA Functions +DECL_FUNC_PTR(LsaConnectUntrusted); +DECL_FUNC_PTR(LsaLookupAuthenticationPackage); +DECL_FUNC_PTR(LsaCallAuthenticationPackage); +DECL_FUNC_PTR(LsaFreeReturnBuffer); +DECL_FUNC_PTR(LsaGetLogonSessionData); + +// CCAPI +FUNC_INFO ccapi_fi[] = { + MAKE_FUNC_INFO(cc_initialize), + MAKE_FUNC_INFO(cc_shutdown), + MAKE_FUNC_INFO(cc_get_NC_info), + MAKE_FUNC_INFO(cc_free_NC_info), + END_FUNC_INFO +}; + +FUNC_INFO k4_fi[] = { + MAKE_FUNC_INFO(get_krb_err_txt_entry), + MAKE_FUNC_INFO(k_isinst), + MAKE_FUNC_INFO(k_isname), + MAKE_FUNC_INFO(k_isrealm), + MAKE_FUNC_INFO(kadm_change_your_password), + MAKE_FUNC_INFO(kname_parse), + MAKE_FUNC_INFO(krb_get_cred), + MAKE_FUNC_INFO(krb_get_krbhst), + MAKE_FUNC_INFO(krb_get_lrealm), + MAKE_FUNC_INFO(krb_get_pw_in_tkt), + MAKE_FUNC_INFO(krb_get_tf_realm), + MAKE_FUNC_INFO(krb_mk_req), + MAKE_FUNC_INFO(krb_realmofhost), + MAKE_FUNC_INFO(tf_init), + MAKE_FUNC_INFO(tf_close), + MAKE_FUNC_INFO(tf_get_cred), + MAKE_FUNC_INFO(tf_get_pname), + MAKE_FUNC_INFO(tf_get_pinst), + MAKE_FUNC_INFO(LocalHostAddr), + MAKE_FUNC_INFO(tkt_string), + MAKE_FUNC_INFO(krb_set_tkt_string), + MAKE_FUNC_INFO(initialize_krb_error_func), + MAKE_FUNC_INFO(initialize_kadm_error_table), + MAKE_FUNC_INFO(dest_tkt), + /* MAKE_FUNC_INFO(lsh_LoadKrb4LeashErrorTables), */// XXX + MAKE_FUNC_INFO(krb_in_tkt), + MAKE_FUNC_INFO(krb_save_credentials), + MAKE_FUNC_INFO(krb_get_krbconf2), + MAKE_FUNC_INFO(krb_get_krbrealm2), + MAKE_FUNC_INFO(krb_life_to_time), + END_FUNC_INFO +}; + +FUNC_INFO k5_fi[] = { + MAKE_FUNC_INFO(krb5_change_password), + MAKE_FUNC_INFO(krb5_get_init_creds_opt_init), + MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_tkt_life), + MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_renew_life), + MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_forwardable), + MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_proxiable), + MAKE_FUNC_INFO(krb5_get_init_creds_opt_set_address_list), + MAKE_FUNC_INFO(krb5_get_init_creds_password), + MAKE_FUNC_INFO(krb5_get_prompt_types), + MAKE_FUNC_INFO(krb5_build_principal_ext), + MAKE_FUNC_INFO(krb5_cc_get_name), + MAKE_FUNC_INFO(krb5_cc_resolve), + MAKE_FUNC_INFO(krb5_cc_default), + MAKE_FUNC_INFO(krb5_cc_default_name), + MAKE_FUNC_INFO(krb5_cc_set_default_name), + MAKE_FUNC_INFO(krb5_cc_initialize), + MAKE_FUNC_INFO(krb5_cc_destroy), + MAKE_FUNC_INFO(krb5_cc_close), + MAKE_FUNC_INFO(krb5_cc_copy_creds), + MAKE_FUNC_INFO(krb5_cc_store_cred), + MAKE_FUNC_INFO(krb5_cc_retrieve_cred), + MAKE_FUNC_INFO(krb5_cc_get_principal), + MAKE_FUNC_INFO(krb5_cc_start_seq_get), + MAKE_FUNC_INFO(krb5_cc_next_cred), + MAKE_FUNC_INFO(krb5_cc_end_seq_get), + MAKE_FUNC_INFO(krb5_cc_remove_cred), + MAKE_FUNC_INFO(krb5_cc_set_flags), + // MAKE_FUNC_INFO(krb5_cc_get_type), + MAKE_FUNC_INFO(krb5_free_context), + MAKE_FUNC_INFO(krb5_free_cred_contents), + MAKE_FUNC_INFO(krb5_free_principal), + MAKE_FUNC_INFO(krb5_get_in_tkt_with_password), + MAKE_FUNC_INFO(krb5_init_context), + MAKE_FUNC_INFO(krb5_parse_name), + MAKE_FUNC_INFO(krb5_timeofday), + MAKE_FUNC_INFO(krb5_timestamp_to_sfstring), + MAKE_FUNC_INFO(krb5_unparse_name), + MAKE_FUNC_INFO(krb5_get_credentials), + MAKE_FUNC_INFO(krb5_mk_req), + MAKE_FUNC_INFO(krb5_sname_to_principal), + MAKE_FUNC_INFO(krb5_get_credentials_renew), + MAKE_FUNC_INFO(krb5_free_data), + MAKE_FUNC_INFO(krb5_free_data_contents), + // MAKE_FUNC_INFO(krb5_get_realm_domain), + MAKE_FUNC_INFO(krb5_free_unparsed_name), + MAKE_FUNC_INFO(krb5_os_localaddr), + MAKE_FUNC_INFO(krb5_copy_keyblock_contents), + MAKE_FUNC_INFO(krb5_copy_data), + MAKE_FUNC_INFO(krb5_free_creds), + MAKE_FUNC_INFO(krb5_build_principal), + MAKE_FUNC_INFO(krb5_get_renewed_creds), + MAKE_FUNC_INFO(krb5_free_addresses), + MAKE_FUNC_INFO(krb5_get_default_config_files), + MAKE_FUNC_INFO(krb5_free_config_files), + MAKE_FUNC_INFO(krb5_get_default_realm), + MAKE_FUNC_INFO(krb5_free_ticket), + MAKE_FUNC_INFO(krb5_decode_ticket), + MAKE_FUNC_INFO(krb5_get_host_realm), + MAKE_FUNC_INFO(krb5_free_host_realm), + MAKE_FUNC_INFO(krb5_c_random_make_octets), + MAKE_FUNC_INFO(krb5_free_default_realm), + END_FUNC_INFO +}; + +FUNC_INFO k524_fi[] = { + MAKE_FUNC_INFO(krb524_init_ets), + MAKE_FUNC_INFO(krb524_convert_creds_kdc), + END_FUNC_INFO +}; + +FUNC_INFO profile_fi[] = { + MAKE_FUNC_INFO(profile_init), + MAKE_FUNC_INFO(profile_release), + MAKE_FUNC_INFO(profile_get_subsection_names), + MAKE_FUNC_INFO(profile_free_list), + MAKE_FUNC_INFO(profile_get_string), + MAKE_FUNC_INFO(profile_release_string), + END_FUNC_INFO +}; + +FUNC_INFO ce_fi[] = { + MAKE_FUNC_INFO(com_err), + MAKE_FUNC_INFO(error_message), + END_FUNC_INFO +}; + +FUNC_INFO service_fi[] = { + MAKE_FUNC_INFO(OpenSCManagerA), + MAKE_FUNC_INFO(OpenServiceA), + MAKE_FUNC_INFO(QueryServiceStatus), + MAKE_FUNC_INFO(CloseServiceHandle), + MAKE_FUNC_INFO(LsaNtStatusToWinError), + END_FUNC_INFO +}; + +FUNC_INFO lsa_fi[] = { + MAKE_FUNC_INFO(LsaConnectUntrusted), + MAKE_FUNC_INFO(LsaLookupAuthenticationPackage), + MAKE_FUNC_INFO(LsaCallAuthenticationPackage), + MAKE_FUNC_INFO(LsaFreeReturnBuffer), + MAKE_FUNC_INFO(LsaGetLogonSessionData), + END_FUNC_INFO +}; + +// psapi functions +DECL_FUNC_PTR(GetModuleFileNameExA); +DECL_FUNC_PTR(EnumProcessModules); + +FUNC_INFO psapi_fi[] = { + MAKE_FUNC_INFO(GetModuleFileNameExA), + MAKE_FUNC_INFO(EnumProcessModules), + END_FUNC_INFO +}; + +// toolhelp functions +DECL_FUNC_PTR(CreateToolhelp32Snapshot); +DECL_FUNC_PTR(Module32First); +DECL_FUNC_PTR(Module32Next); + +FUNC_INFO toolhelp_fi[] = { + MAKE_FUNC_INFO(CreateToolhelp32Snapshot), + MAKE_FUNC_INFO(Module32First), + MAKE_FUNC_INFO(Module32Next), + END_FUNC_INFO +}; + +khm_int32 init_imports(void) { + OSVERSIONINFO osvi; + + LoadFuncs(KRB4_DLL, k4_fi, &hKrb4, 0, 1, 0, 0); + LoadFuncs(KRB5_DLL, k5_fi, &hKrb5, 0, 1, 0, 0); + LoadFuncs(COMERR_DLL, ce_fi, &hComErr, 0, 0, 1, 0); + LoadFuncs(SERVICE_DLL, service_fi, &hService, 0, 1, 0, 0); + LoadFuncs(SECUR32_DLL, lsa_fi, &hSecur32, 0, 1, 1, 1); + LoadFuncs(KRB524_DLL, k524_fi, &hKrb524, 0, 1, 1, 1); + LoadFuncs(PROFILE_DLL, profile_fi, &hProfile, 0, 1, 0, 0); + LoadFuncs(CCAPI_DLL, ccapi_fi, &hCCAPI, 0, 1, 0, 0); + + memset(&osvi, 0, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osvi); + + // XXX: We should really use feature testing, first + // checking for CreateToolhelp32Snapshot. If that's + // not around, we try the psapi stuff. + // + // Only load LSA functions if on NT/2000/XP + if(osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) + { + // Windows 9x + LoadFuncs(TOOLHELPDLL, toolhelp_fi, &hToolHelp32, 0, 1, 0, 0); + hPsapi = 0; + } + else if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) + { + // Windows NT + LoadFuncs(PSAPIDLL, psapi_fi, &hPsapi, 0, 1, 0, 0); + hToolHelp32 = 0; + } + + AfsAvailable = TRUE; //afscompat_init(); + + return KHM_ERROR_SUCCESS; +} + +khm_int32 exit_imports(void) { + //afscompat_close(); + + if (hKrb4) + FreeLibrary(hKrb4); + if (hKrb5) + FreeLibrary(hKrb5); + if (hProfile) + FreeLibrary(hProfile); + if (hComErr) + FreeLibrary(hComErr); + if (hService) + FreeLibrary(hService); + if (hSecur32) + FreeLibrary(hSecur32); + if (hKrb524) + FreeLibrary(hKrb524); + if (hPsapi) + FreeLibrary(hPsapi); + if (hToolHelp32) + FreeLibrary(hToolHelp32); + + return KHM_ERROR_SUCCESS; +} + +int (*Lcom_err)(LPSTR,long,LPSTR,...); +LPSTR (*Lerror_message)(long); +LPSTR (*Lerror_table_name)(long); + +void Leash_load_com_err_callback(FARPROC ce, + FARPROC em, + FARPROC etn) +{ + (FARPROC)Lcom_err=ce; + (FARPROC)Lerror_message=em; + (FARPROC)Lerror_table_name=etn; +} diff --git a/src/windows/identity/plugins/common/dynimport.h b/src/windows/identity/plugins/common/dynimport.h new file mode 100644 index 000000000..b3ba225a6 --- /dev/null +++ b/src/windows/identity/plugins/common/dynimport.h @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_DYNIMPORT_H +#define __KHIMAIRA_DYNIMPORT_H + +/* Dynamic imports */ +#include +#include +#include + +extern HINSTANCE hKrb4; +extern HINSTANCE hKrb5; +extern HINSTANCE hProfile; + +/////////////////////////////////////////////////////////////////////////////// + +#define CCAPI_DLL "krbcc32.dll" +#define KRBCC32_DLL "krbcc32.dll" +#define SERVICE_DLL "advapi32.dll" +#define SECUR32_DLL "secur32.dll" +#define PROFILE_DLL "xpprof32.dll" + +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include + +//// CCAPI +/* In order to avoid including the private CCAPI headers */ +typedef int cc_int32; + +#define CC_API_VER_1 1 +#define CC_API_VER_2 2 + +#define CCACHE_API cc_int32 + +/* +** The Official Error Codes +*/ +#define CC_NOERROR 0 +#define CC_BADNAME 1 +#define CC_NOTFOUND 2 +#define CC_END 3 +#define CC_IO 4 +#define CC_WRITE 5 +#define CC_NOMEM 6 +#define CC_FORMAT 7 +#define CC_LOCKED 8 +#define CC_BAD_API_VERSION 9 +#define CC_NO_EXIST 10 +#define CC_NOT_SUPP 11 +#define CC_BAD_PARM 12 +#define CC_ERR_CACHE_ATTACH 13 +#define CC_ERR_CACHE_RELEASE 14 +#define CC_ERR_CACHE_FULL 15 +#define CC_ERR_CRED_VERSION 16 + +enum { + CC_CRED_VUNKNOWN = 0, // For validation + CC_CRED_V4 = 1, + CC_CRED_V5 = 2, + CC_CRED_VMAX = 3 // For validation +}; + +typedef struct opaque_dll_control_block_type* apiCB; +typedef struct _infoNC { + char* name; + char* principal; + cc_int32 vers; +} infoNC; + +TYPEDEF_FUNC( +CCACHE_API, +CALLCONV_C, +cc_initialize, + ( + apiCB** cc_ctx, // < DLL's primary control structure. + // returned here, passed everywhere else + cc_int32 api_version, // > ver supported by caller (use CC_API_VER_1) + cc_int32* api_supported, // < if ~NULL, max ver supported by DLL + const char** vendor // < if ~NULL, vendor name in read only C string + ) +); + +TYPEDEF_FUNC( +CCACHE_API, +CALLCONV_C, +cc_shutdown, + ( + apiCB** cc_ctx // <> DLL's primary control structure. NULL after + ) +); + +TYPEDEF_FUNC( +CCACHE_API, +CALLCONV_C, +cc_get_NC_info, + ( + apiCB* cc_ctx, // > DLL's primary control structure + struct _infoNC*** ppNCi // < (NULL before call) null terminated, + // list of a structs (free via cc_free_infoNC()) + ) +); + +TYPEDEF_FUNC( +CCACHE_API, +CALLCONV_C, +cc_free_NC_info, + ( + apiCB* cc_ctx, + struct _infoNC*** ppNCi // < free list of structs returned by + // cc_get_cache_names(). set to NULL on return + ) +); +//// \CCAPI + +extern DWORD AfsAvailable; + +// service definitions +typedef SC_HANDLE (WINAPI *FP_OpenSCManagerA)(char *, char *, DWORD); +typedef SC_HANDLE (WINAPI *FP_OpenServiceA)(SC_HANDLE, char *, DWORD); +typedef BOOL (WINAPI *FP_QueryServiceStatus)(SC_HANDLE, LPSERVICE_STATUS); +typedef BOOL (WINAPI *FP_CloseServiceHandle)(SC_HANDLE); + +////////////////////////////////////////////////////////////////////////////// + +// CCAPI +extern DECL_FUNC_PTR(cc_initialize); +extern DECL_FUNC_PTR(cc_shutdown); +extern DECL_FUNC_PTR(cc_get_NC_info); +extern DECL_FUNC_PTR(cc_free_NC_info); + +// krb4 functions +extern DECL_FUNC_PTR(get_krb_err_txt_entry); +extern DECL_FUNC_PTR(k_isinst); +extern DECL_FUNC_PTR(k_isname); +extern DECL_FUNC_PTR(k_isrealm); +extern DECL_FUNC_PTR(kadm_change_your_password); +extern DECL_FUNC_PTR(kname_parse); +extern DECL_FUNC_PTR(krb_get_cred); +extern DECL_FUNC_PTR(krb_get_krbhst); +extern DECL_FUNC_PTR(krb_get_lrealm); +extern DECL_FUNC_PTR(krb_get_pw_in_tkt); +extern DECL_FUNC_PTR(krb_get_tf_realm); +extern DECL_FUNC_PTR(krb_mk_req); +extern DECL_FUNC_PTR(krb_realmofhost); +extern DECL_FUNC_PTR(tf_init); +extern DECL_FUNC_PTR(tf_close); +extern DECL_FUNC_PTR(tf_get_cred); +extern DECL_FUNC_PTR(tf_get_pname); +extern DECL_FUNC_PTR(tf_get_pinst); +extern DECL_FUNC_PTR(LocalHostAddr); +extern DECL_FUNC_PTR(tkt_string); +extern DECL_FUNC_PTR(krb_set_tkt_string); +extern DECL_FUNC_PTR(initialize_krb_error_func); +extern DECL_FUNC_PTR(initialize_kadm_error_table); +extern DECL_FUNC_PTR(dest_tkt); +extern DECL_FUNC_PTR(lsh_LoadKrb4LeashErrorTables); // XXX +extern DECL_FUNC_PTR(krb_in_tkt); +extern DECL_FUNC_PTR(krb_save_credentials); +extern DECL_FUNC_PTR(krb_get_krbconf2); +extern DECL_FUNC_PTR(krb_get_krbrealm2); +extern DECL_FUNC_PTR(krb_life_to_time); + +// krb5 functions +extern DECL_FUNC_PTR(krb5_change_password); +extern DECL_FUNC_PTR(krb5_get_init_creds_opt_init); +extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_tkt_life); +extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_renew_life); +extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_forwardable); +extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_proxiable); +extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_renew_life); +extern DECL_FUNC_PTR(krb5_get_init_creds_opt_set_address_list); +extern DECL_FUNC_PTR(krb5_get_init_creds_password); +extern DECL_FUNC_PTR(krb5_get_prompt_types); +extern DECL_FUNC_PTR(krb5_build_principal_ext); +extern DECL_FUNC_PTR(krb5_cc_get_name); +extern DECL_FUNC_PTR(krb5_cc_resolve); +extern DECL_FUNC_PTR(krb5_cc_default); +extern DECL_FUNC_PTR(krb5_cc_default_name); +extern DECL_FUNC_PTR(krb5_cc_set_default_name); +extern DECL_FUNC_PTR(krb5_cc_initialize); +extern DECL_FUNC_PTR(krb5_cc_destroy); +extern DECL_FUNC_PTR(krb5_cc_close); +extern DECL_FUNC_PTR(krb5_cc_copy_creds); +extern DECL_FUNC_PTR(krb5_cc_store_cred); +extern DECL_FUNC_PTR(krb5_cc_retrieve_cred); +extern DECL_FUNC_PTR(krb5_cc_get_principal); +extern DECL_FUNC_PTR(krb5_cc_start_seq_get); +extern DECL_FUNC_PTR(krb5_cc_next_cred); +extern DECL_FUNC_PTR(krb5_cc_end_seq_get); +extern DECL_FUNC_PTR(krb5_cc_remove_cred); +extern DECL_FUNC_PTR(krb5_cc_set_flags); +// extern DECL_FUNC_PTR(krb5_cc_get_type); +extern DECL_FUNC_PTR(krb5_free_context); +extern DECL_FUNC_PTR(krb5_free_cred_contents); +extern DECL_FUNC_PTR(krb5_free_principal); +extern DECL_FUNC_PTR(krb5_get_in_tkt_with_password); +extern DECL_FUNC_PTR(krb5_init_context); +extern DECL_FUNC_PTR(krb5_parse_name); +extern DECL_FUNC_PTR(krb5_timeofday); +extern DECL_FUNC_PTR(krb5_timestamp_to_sfstring); +extern DECL_FUNC_PTR(krb5_unparse_name); +extern DECL_FUNC_PTR(krb5_get_credentials); +extern DECL_FUNC_PTR(krb5_mk_req); +extern DECL_FUNC_PTR(krb5_sname_to_principal); +extern DECL_FUNC_PTR(krb5_get_credentials_renew); +extern DECL_FUNC_PTR(krb5_free_data); +extern DECL_FUNC_PTR(krb5_free_data_contents); +// extern DECL_FUNC_PTR(krb5_get_realm_domain); +extern DECL_FUNC_PTR(krb5_free_unparsed_name); +extern DECL_FUNC_PTR(krb5_os_localaddr); +extern DECL_FUNC_PTR(krb5_copy_keyblock_contents); +extern DECL_FUNC_PTR(krb5_copy_data); +extern DECL_FUNC_PTR(krb5_free_creds); +extern DECL_FUNC_PTR(krb5_build_principal); +extern DECL_FUNC_PTR(krb5_get_renewed_creds); +extern DECL_FUNC_PTR(krb5_free_addresses); +extern DECL_FUNC_PTR(krb5_get_default_config_files); +extern DECL_FUNC_PTR(krb5_free_config_files); +extern DECL_FUNC_PTR(krb5_get_default_realm); +extern DECL_FUNC_PTR(krb5_free_ticket); +extern DECL_FUNC_PTR(krb5_decode_ticket); +extern DECL_FUNC_PTR(krb5_get_host_realm); +extern DECL_FUNC_PTR(krb5_free_host_realm); +extern DECL_FUNC_PTR(krb5_c_random_make_octets); +extern DECL_FUNC_PTR(krb5_free_default_realm); + +// Krb524 functions +extern DECL_FUNC_PTR(krb524_init_ets); +extern DECL_FUNC_PTR(krb524_convert_creds_kdc); + +// ComErr functions +extern DECL_FUNC_PTR(com_err); +extern DECL_FUNC_PTR(error_message); + +// Profile functions +extern DECL_FUNC_PTR(profile_init); +extern DECL_FUNC_PTR(profile_release); +extern DECL_FUNC_PTR(profile_get_subsection_names); +extern DECL_FUNC_PTR(profile_free_list); +extern DECL_FUNC_PTR(profile_get_string); +extern DECL_FUNC_PTR(profile_release_string); + +// Service functions +extern DECL_FUNC_PTR(OpenSCManagerA); +extern DECL_FUNC_PTR(OpenServiceA); +extern DECL_FUNC_PTR(QueryServiceStatus); +extern DECL_FUNC_PTR(CloseServiceHandle); +extern DECL_FUNC_PTR(LsaNtStatusToWinError); + +// LSA Functions +extern DECL_FUNC_PTR(LsaConnectUntrusted); +extern DECL_FUNC_PTR(LsaLookupAuthenticationPackage); +extern DECL_FUNC_PTR(LsaCallAuthenticationPackage); +extern DECL_FUNC_PTR(LsaFreeReturnBuffer); +extern DECL_FUNC_PTR(LsaGetLogonSessionData); + +// toolhelp functions +TYPEDEF_FUNC( + HANDLE, + WINAPI, + CreateToolhelp32Snapshot, + (DWORD, DWORD) + ); +TYPEDEF_FUNC( + BOOL, + WINAPI, + Module32First, + (HANDLE, LPMODULEENTRY32) + ); +TYPEDEF_FUNC( + BOOL, + WINAPI, + Module32Next, + (HANDLE, LPMODULEENTRY32) + ); + +// psapi functions +TYPEDEF_FUNC( + DWORD, + WINAPI, + GetModuleFileNameExA, + (HANDLE, HMODULE, LPSTR, DWORD) + ); + +TYPEDEF_FUNC( + BOOL, + WINAPI, + EnumProcessModules, + (HANDLE, HMODULE*, DWORD, LPDWORD) + ); + +#define pGetModuleFileNameEx pGetModuleFileNameExA +#define TOOLHELPDLL "kernel32.dll" +#define PSAPIDLL "psapi.dll" + +// psapi functions +extern DECL_FUNC_PTR(GetModuleFileNameExA); +extern DECL_FUNC_PTR(EnumProcessModules); + +// toolhelp functions +extern DECL_FUNC_PTR(CreateToolhelp32Snapshot); +extern DECL_FUNC_PTR(Module32First); +extern DECL_FUNC_PTR(Module32Next); + +khm_int32 init_imports(void); +khm_int32 exit_imports(void); + +#endif diff --git a/src/windows/identity/plugins/common/krb5common.c b/src/windows/identity/plugins/common/krb5common.c new file mode 100644 index 000000000..5501a1206 --- /dev/null +++ b/src/windows/identity/plugins/common/krb5common.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include +#include +#include + +/**************************************/ +/* khm_krb5_error(): */ +/**************************************/ +int +khm_krb5_error(krb5_error_code rc, LPCSTR FailedFunctionName, + int FreeContextFlag, krb5_context * ctx, + krb5_ccache * cache) +{ +#ifdef NO_KRB5 + return 0; +#else + +#ifdef SHOW_MESSAGE_IN_AN_ANNOYING_WAY + char message[256]; + const char *errText; + int krb5Error = ((int)(rc & 255)); + + errText = perror_message(rc); + _snprintf(message, sizeof(message), + "%s\n(Kerberos error %ld)\n\n%s failed", + errText, + krb5Error, + FailedFunctionName); + + MessageBoxA(NULL, message, "Kerberos Five", MB_OK | MB_ICONERROR | + MB_TASKMODAL | + MB_SETFOREGROUND); +#endif + + if (FreeContextFlag == 1) + { + if (*ctx != NULL) + { + if (*cache != NULL) { + pkrb5_cc_close(*ctx, *cache); + *cache = NULL; + } + + pkrb5_free_context(*ctx); + *ctx = NULL; + } + } + + return rc; + +#endif //!NO_KRB5 +} + +int +khm_krb5_initialize(khm_handle ident, + krb5_context *ctx, + krb5_ccache *cache) +{ +#ifdef NO_KRB5 + return(0); +#else + + LPCSTR functionName; + int freeContextFlag; + krb5_error_code rc; + krb5_flags flags = 0; + + if (pkrb5_init_context == NULL) + return 1; + + if (*ctx == 0 && (rc = (*pkrb5_init_context)(ctx))) + { + functionName = "krb5_init_context()"; + freeContextFlag = 0; + goto on_error; + } + + if(*cache == 0) { + wchar_t wccname[256]; + khm_size cbwccname; + + if(ident != NULL) { + cbwccname = sizeof(wccname); + do { + char ccname[256]; + + if(KHM_FAILED(kcdb_identity_get_attrib(ident, L"Krb5CCName", NULL, wccname, &cbwccname))) + break; + + if(UnicodeStrToAnsi(ccname, sizeof(ccname), wccname) == 0) + break; + + if((*pkrb5_cc_resolve)(*ctx, ccname, cache)) { + functionName = "krb5_cc_resolve()"; + freeContextFlag = 1; + goto on_error; + } + } while(FALSE); + } + + if (*cache == 0 && (rc = (*pkrb5_cc_default)(*ctx, cache))) + { + functionName = "krb5_cc_default()"; + freeContextFlag = 1; + goto on_error; + } + } + +#ifdef KRB5_TC_NOTICKET + flags = KRB5_TC_NOTICKET; +#endif + + if ((rc = (*pkrb5_cc_set_flags)(*ctx, *cache, flags))) + { + if (rc != KRB5_FCC_NOFILE && rc != KRB5_CC_NOTFOUND) + khm_krb5_error(rc, "krb5_cc_set_flags()", 0, ctx, + cache); + else if ((rc == KRB5_FCC_NOFILE || rc == KRB5_CC_NOTFOUND) && *ctx != NULL) + { + if (*cache != NULL) + (*pkrb5_cc_close)(*ctx, *cache); + } + return rc; + } + return 0; + +on_error: + return khm_krb5_error(rc, functionName, freeContextFlag, ctx, cache); +#endif //!NO_KRB5 +} diff --git a/src/windows/identity/plugins/common/krb5common.h b/src/windows/identity/plugins/common/krb5common.h new file mode 100644 index 000000000..7d998215a --- /dev/null +++ b/src/windows/identity/plugins/common/krb5common.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/* Adapted from multiple Leash header files */ + +#ifndef __KHIMAIRA_KRB5COMMON_H +#define __KHIMAIRA_KRB5COMMON_H + +#include + +#ifndef NO_KRB5 +int khm_krb5_error(krb5_error_code rc, LPCSTR FailedFunctionName, + int FreeContextFlag, krb5_context *ctx, + krb5_ccache *cache); + + +int khm_krb5_initialize(khm_handle ident, krb5_context *, krb5_ccache *); +#endif /* NO_KRB5 */ + +#endif \ No newline at end of file diff --git a/src/windows/identity/plugins/krb4/Makefile b/src/windows/identity/plugins/krb4/Makefile new file mode 100644 index 000000000..d6b749192 --- /dev/null +++ b/src/windows/identity/plugins/krb4/Makefile @@ -0,0 +1,78 @@ +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +MODULE=plugins\krb4 +!include <../../config/Makefile.w32> + +DLLFILE=$(BINDIR)\krb4cred.dll + +LIBFILE=$(LIBDIR)\krb4cred.lib + +OBJFILES= \ + $(LIBDIR)\dynimport.obj \ + $(LIBDIR)\krb5common.obj \ + $(OBJ)\main.obj \ + $(OBJ)\krb4plugin.obj \ + $(OBJ)\krb4funcs.obj \ + $(OBJ)\errorfuncs.obj \ + $(OBJ)\krb4config.obj \ + $(OBJ)\krb4configdlg.obj + +LIBFILES= \ + $(LIBDIR)\nidmgr32.lib \ + $(KFWLIBDIR)\loadfuncs.lib + +SDKLIBFILES= + +$(OBJ)\krb4config.c: krbconfig.csv $(CONFDIR)\csvschema.cfg + $(CCSV) $** $@ + +$(DLLFILE): $(OBJFILES) + $(DLLGUILINK) $(LIBFILES) $(SDKLIBFILES) + +all: mkdirs $(DLLFILE) lang + +lang:: + +# Repeat this block as necessary redefining LANG for additional +# languages. + +# Begin language block +LANG=en_us + +LANGDLL=$(BINDIR)\krb4cred_$(LANG).dll + +lang:: $(LANGDLL) + +$(LANGDLL): $(OBJ)\langres_$(LANG).res + $(DLLRESLINK) + +$(OBJ)\langres_$(LANG).res: lang\$(LANG)\langres.rc + $(RC2RES) +# End language block + +clean:: +!if defined(INCFILES) + $(RM) $(INCFILES) +!endif diff --git a/src/windows/identity/plugins/krb4/datarep.h b/src/windows/identity/plugins/krb4/datarep.h new file mode 100644 index 000000000..9c7048e05 --- /dev/null +++ b/src/windows/identity/plugins/krb4/datarep.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KRB_DATAREP_H +#define __KHIMAIRA_KRB_DATAREP_H + + +khm_int32 KHMAPI enctype_toString(const void * data, khm_int32 cbdata, wchar_t *destbuf, khm_int32 *pcbdestbuf, khm_int32 flags); +khm_int32 KHMAPI addr_list_toString(const void *, khm_int32, wchar_t *, khm_int32 *, khm_int32); +khm_int32 KHMAPI krb5flags_toString(const void *, khm_int32, wchar_t *, khm_int32 *, khm_int32); +khm_int32 KHMAPI renew_for_cb(khm_handle cred, khm_int32 id, void * buffer, khm_int32 * pcbsize); + + +#endif \ No newline at end of file diff --git a/src/windows/identity/plugins/krb4/errorfuncs.c b/src/windows/identity/plugins/krb4/errorfuncs.c new file mode 100644 index 000000000..9feaad2a7 --- /dev/null +++ b/src/windows/identity/plugins/krb4/errorfuncs.c @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include + +extern void (__cdecl *pinitialize_krb_error_func)(); +extern void (__cdecl *pinitialize_kadm_error_table)(); + + +khm_int32 init_error_funcs() +{ + +#if 0 + /*TODO: Do something about this */ + if (plsh_LoadKrb4LeashErrorTables) + plsh_LoadKrb4LeashErrorTables(hLeashInst, 0); +#endif + return KHM_ERROR_SUCCESS; +} + +khm_int32 exit_error_funcs() +{ + return KHM_ERROR_SUCCESS; +} + +// Global Variables. +static long lsh_errno; +static char *err_context; /* error context */ +extern int (*Lcom_err)(LPSTR,long,LPSTR,...); +extern LPSTR (*Lerror_message)(long); +extern LPSTR (*Lerror_table_name)(long); + +#ifdef WIN16 +#define UNDERSCORE "_" +#else +#define UNDERSCORE +#endif + +HWND GetRootParent (HWND Child) +{ + HWND Last; + while (Child) + { + Last = Child; + Child = GetParent (Child); + } + return Last; +} + + +LPSTR err_describe(LPSTR buf, long code) +{ + LPSTR cp, com_err_msg; + int offset; + long table_num; + char *etype; + + offset = (int) (code & 255); + table_num = code - offset; + com_err_msg = Lerror_message(code); + + switch(table_num) + { + case krb_err_base: + case kadm_err_base: + break; + default: + strcpy(buf, com_err_msg); + return buf; + } + + cp = buf; + if (table_num == krb_err_base) + switch(offset) + { + case KDC_NAME_EXP: /* 001 Principal expired */ + case KDC_SERVICE_EXP: /* 002 Service expired */ + case KDC_AUTH_EXP: /* 003 Auth expired */ + case KDC_PKT_VER: /* 004 Protocol version unknown */ + case KDC_P_MKEY_VER: /* 005 Wrong master key version */ + case KDC_S_MKEY_VER: /* 006 Wrong master key version */ + case KDC_BYTE_ORDER: /* 007 Byte order unknown */ + case KDC_PR_N_UNIQUE: /* 009 Principal not unique */ + case KDC_NULL_KEY: /* 010 Principal has null key */ + case KDC_GEN_ERR: /* 011 Generic error from KDC */ + case INTK_W_NOTALL : /* 061 Not ALL tickets returned */ + case INTK_PROT : /* 063 Protocol Error */ + case INTK_ERR : /* 070 Other error */ + com_err_msg = "Something weird happened... try again, and if Leash" + " continues to fail, contact Network Services as listed in the " + "About box."; + break; + case KDC_PR_UNKNOWN: /* 008 Principal unknown */ + com_err_msg = "You have entered an unknown username/instance/realm" + " combination."; + break; + case GC_TKFIL : /* 021 Can't read ticket file */ + case GC_NOTKT : /* 022 Can't find ticket or TGT */ + com_err_msg = "Something is wrong with the memory where your " + "tickets are stored. Try exiting Windows and restarting your " + "computer."; + break; + case MK_AP_TGTEXP : /* 026 TGT Expired */ + /* no extra error msg */ + break; + case RD_AP_TIME : /* 037 delta_t too big */ + com_err_msg = "Your computer's clock is out of sync with the " + "Kerberos server. Please see the help file about correcting " + "your clock."; + break; + + case RD_AP_UNDEC : /* 031 Can't decode authenticator */ + case RD_AP_EXP : /* 032 Ticket expired */ + case RD_AP_NYV : /* 033 Ticket not yet valid */ + case RD_AP_REPEAT : /* 034 Repeated request */ + case RD_AP_NOT_US : /* 035 The ticket isn't for us */ + case RD_AP_INCON : /* 036 Request is inconsistent */ + case RD_AP_BADD : /* 038 Incorrect net address */ + case RD_AP_VERSION : /* 039 protocol version mismatch */ + case RD_AP_MSG_TYPE : /* 040 invalid msg type */ + case RD_AP_MODIFIED : /* 041 message stream modified */ + case RD_AP_ORDER : /* 042 message out of order */ + case RD_AP_UNAUTHOR : /* 043 unauthorized request */ + /* no extra error msg */ + break; + case GT_PW_NULL: /* 51 Current PW is null */ + case GT_PW_BADPW: /* 52 Incorrect current password */ + case GT_PW_PROT: /* 53 Protocol Error */ + case GT_PW_KDCERR: /* 54 Error returned by KDC */ + case GT_PW_NULLTKT: /* 55 Null tkt returned by KDC */ + /* no error msg yet */ + break; + + /* Values returned by send_to_kdc */ + case SKDC_RETRY : /* 56 Retry count exceeded */ + case SKDC_CANT : /* 57 Can't send request */ + com_err_msg = "Cannot contact the kerberos server for the selected realm."; + break; + /* no error message on purpose: */ + case INTK_BADPW : /* 062 Incorrect password */ + break; + default: + /* no extra error msg */ + break; + } + else + switch(code) + { + case KADM_INSECURE_PW: + /* if( kadm_info != NULL ){ + * wsprintf(buf, "%s\n%s", com_err_msg, kadm_info); + * } else { + * wsprintf(buf, "%s\nPlease see the help file for information " + * "about secure passwords.", com_err_msg); + * } + * com_err_msg = buf; + */ + + /* The above code would be preferred since it allows site specific + * information to be delivered from the Kerberos server. However the + * message box is too small for VGA screens. + * It does work well if we only have to support 1024x768 + */ + + com_err_msg = "You have entered an insecure or weak password."; + + default: + /* no extra error msg */ + break; + } + if(com_err_msg != buf) + strcpy(buf, com_err_msg); + cp = buf + strlen(buf); + *cp++ = '\n'; + switch(table_num) { + case krb_err_base: + etype = "Kerberos"; + break; + case kadm_err_base: + etype = "Kerberos supplemental"; + break; + default: + etype = Lerror_table_name(table_num); + break; + } + wsprintfA((LPSTR) cp, (LPSTR) "(%s error %d" +#ifdef DEBUG_COM_ERR + " (absolute error %ld)" +#endif + ")", etype, offset + //")\nPress F1 for help on this error.", etype, offset +#ifdef DEBUG_COM_ERR + , code +#endif + ); + + return (LPSTR)buf; +} + +int lsh_com_err_proc (LPSTR whoami, long code, + LPSTR fmt, va_list args) +{ + int retval; + HWND hOldFocus; + char buf[1024], *cp; /* changed to 512 by jms 8/23/93 */ + WORD mbformat = MB_OK | MB_ICONEXCLAMATION; + + cp = buf; + memset(buf, '\0', sizeof(buf)); + cp[0] = '\0'; + + if (code) + { + err_describe(buf, code); + while (*cp) + cp++; + } + + if (fmt) + { + if (fmt[0] == '%' && fmt[1] == 'b') + { + fmt += 2; + mbformat = va_arg(args, WORD); + /* if the first arg is a %b, we use it for the message + box MB_??? flags. */ + } + if (code) + { + *cp++ = '\n'; + *cp++ = '\n'; + } + wvsprintfA((LPSTR)cp, fmt, args); + } + hOldFocus = GetFocus(); + retval = MessageBoxA(/*GetRootParent(hOldFocus)*/NULL, buf, whoami, + mbformat | MB_ICONHAND | MB_TASKMODAL); + SetFocus(hOldFocus); + return retval; +} diff --git a/src/windows/identity/plugins/krb4/errorfuncs.h b/src/windows/identity/plugins/krb4/errorfuncs.h new file mode 100644 index 000000000..be8f4e7c5 --- /dev/null +++ b/src/windows/identity/plugins/krb4/errorfuncs.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_ERR_H +#define __KHIMAIRA_ERR_H + +/* All error handling and reporting related functions for the krb4/5 + and AFS plugins */ + +#include +#include +/* + * This is a hack needed because the real com_err.h does + * not define err_func. We need it in the case where + * we pull in the real com_err instead of the krb4 + * impostor. + */ +#ifndef _DCNS_MIT_COM_ERR_H +typedef LPSTR (*err_func)(int, long); +#endif + +#include +extern void Leash_initialize_krb_error_func(err_func func,struct et_list **); +#undef init_krb_err_func +#define init_krb_err_func(erf) Leash_initialize_krb_error_func(erf,&_et_list) + +#include + +extern void Leash_initialize_kadm_error_table(struct et_list **); +#undef init_kadm_err_tbl +#define init_kadm_err_tbl() Leash_initialize_kadm_error_table(&_et_list) +#define kadm_err_base ERROR_TABLE_BASE_kadm + +#define krb_err_func Leash_krb_err_func + +#include +int lsh_com_err_proc (LPSTR whoami, long code, + LPSTR fmt, va_list args); +void FAR Leash_load_com_err_callback(FARPROC,FARPROC,FARPROC); + +#ifndef KRBERR +#define KRBERR(code) (code + krb_err_base) +#endif + +int lsh_com_err_proc (LPSTR whoami, long code, LPSTR fmt, va_list args); +int DoNiftyErrorReport(long errnum, LPSTR what); + +LPSTR err_describe(LPSTR buf, long code); + + +/* */ +khm_int32 init_error_funcs(); + +khm_int32 exit_error_funcs(); + + +#endif diff --git a/src/windows/identity/plugins/krb4/krb4configdlg.c b/src/windows/identity/plugins/krb4/krb4configdlg.c new file mode 100644 index 000000000..9ad340698 --- /dev/null +++ b/src/windows/identity/plugins/krb4/krb4configdlg.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include +#include +#include + +INT_PTR CALLBACK +krb4_confg_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + + switch(uMsg) { + case WM_INITDIALOG: + { + wchar_t wbuf[MAX_PATH]; + CHAR krb_path[MAX_PATH]; + CHAR krbrealm_path[MAX_PATH]; + CHAR ticketName[MAX_PATH]; + char * pticketName; + unsigned int krb_path_sz = sizeof(krb_path); + unsigned int krbrealm_path_sz = sizeof(krbrealm_path); + + // Set KRB.CON + memset(krb_path, '\0', sizeof(krb_path)); + if (!pkrb_get_krbconf2(krb_path, &krb_path_sz)) { + // Error has happened + } else { // normal find + AnsiStrToUnicode(wbuf, sizeof(wbuf), krb_path); + SetDlgItemText(hwnd, IDC_CFG_CFGPATH, wbuf); + } + + // Set KRBREALM.CON + memset(krbrealm_path, '\0', sizeof(krbrealm_path)); + if (!pkrb_get_krbrealm2(krbrealm_path, &krbrealm_path_sz)) { + // Error has happened + } else { + AnsiStrToUnicode(wbuf, sizeof(wbuf), krbrealm_path); + SetDlgItemText(hwnd, IDC_CFG_RLMPATH, wbuf); + } + + // Set TICKET.KRB file Editbox + *ticketName = 0; + pkrb_set_tkt_string(0); + + pticketName = ptkt_string(); + if (pticketName) + StringCbCopyA(ticketName, sizeof(ticketName), pticketName); + + if (!*ticketName) { + // error + } else { + AnsiStrToUnicode(wbuf, sizeof(wbuf), ticketName); + SetDlgItemText(hwnd, IDC_CFG_CACHE, wbuf); + } + } + break; + + case WM_DESTROY: + break; + } + return FALSE; +} diff --git a/src/windows/identity/plugins/krb4/krb4funcs.c b/src/windows/identity/plugins/krb4/krb4funcs.c new file mode 100644 index 000000000..8fda720b3 --- /dev/null +++ b/src/windows/identity/plugins/krb4/krb4funcs.c @@ -0,0 +1,505 @@ +/* +* Copyright (c) 2004 Massachusetts Institute of Technology +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, +* modify, merge, publish, distribute, sublicense, and/or sell copies +* of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +/* $Id$ */ + +/* Originally this was krb5routines.c in Leash sources. Subsequently +modified and adapted for NetIDMgr */ + +#include +#include + +#define SECURITY_WIN32 +#include +#include + +#include +#include +#include +#include + + + +int com_addr(void) +{ + long ipAddr; + char loc_addr[ADDR_SZ]; + CREDENTIALS cred; + char service[40]; + char instance[40]; + // char addr[40]; + char realm[40]; + struct in_addr LocAddr; + int k_errno; + + if (pkrb_get_cred == NULL) + return(KSUCCESS); + + k_errno = (*pkrb_get_cred)(service,instance,realm,&cred); + if (k_errno) + return KRBERR(k_errno); + + while(1) { + ipAddr = (*pLocalHostAddr)(); + LocAddr.s_addr = ipAddr; + StringCbCopyA(loc_addr, sizeof(loc_addr), inet_ntoa(LocAddr)); + if ( strcmp(cred.address,loc_addr) != 0) { + /* TODO: do something about this */ + //Leash_kdestroy (); + break; + } + break; + } // while() + return 0; +} + + +long +khm_krb4_list_tickets(void) +{ + char pname[ANAME_SZ]; + char pinst[INST_SZ]; + char prealm[REALM_SZ]; + wchar_t wbuf[256]; + int k_errno; + CREDENTIALS c; + int newtickets = 0; + int open = 0; + khm_handle ident = NULL; + khm_handle cred = NULL; + time_t tt; + FILETIME ft; + + // Since krb_get_tf_realm will return a ticket_file error, + // we will call tf_init and tf_close first to filter out + // things like no ticket file. Otherwise, the error that + // the user would see would be + // klist: can't find realm of ticket file: No ticket file (tf_util) + // instead of klist: No ticket file (tf_util) + if (ptf_init == NULL) + return(KSUCCESS); + + com_addr(); + + // Open ticket file + if ((k_errno = (*ptf_init)((*ptkt_string)(), R_TKT_FIL))) + { + goto cleanup; + } + // Close ticket file + (void) (*ptf_close)(); + + // We must find the realm of the ticket file here before calling + // tf_init because since the realm of the ticket file is not + // really stored in the principal section of the file, the + // routine we use must itself call tf_init and tf_close. + + if ((k_errno = (*pkrb_get_tf_realm)((*ptkt_string)(), prealm)) != KSUCCESS) + { + goto cleanup; + } + + // Open ticket file + if (k_errno = (*ptf_init)((*ptkt_string)(), R_TKT_FIL)) + { + goto cleanup; + } + + open = 1; + + // Get principal name and instance + if ((k_errno = (*ptf_get_pname)(pname)) || (k_errno = (*ptf_get_pinst)(pinst))) + { + goto cleanup; + } + + // You may think that this is the obvious place to get the + // realm of the ticket file, but it can't be done here as the + // routine to do this must open the ticket file. This is why + // it was done before tf_init. + StringCbPrintf(wbuf, sizeof(wbuf), L"%S%S%S%S%S", (LPSTR)pname, + (LPSTR)(pinst[0] ? "." : ""), (LPSTR)pinst, + (LPSTR)(prealm[0] ? "@" : ""), (LPSTR)prealm); + + if(KHM_FAILED(kcdb_identity_create(wbuf, KCDB_IDENT_FLAG_CREATE, &ident))) + { + goto cleanup; + } + + kcdb_credset_flush(krb4_credset); + + // Get KRB4 tickets + while ((k_errno = (*ptf_get_cred)(&c)) == KSUCCESS) + { + StringCbPrintf(wbuf, sizeof(wbuf), L"%S%S%S%S%S", + c.service, + (c.instance[0] ? "." : ""), + c.instance, + (c.realm[0] ? "@" : ""), + c.realm); + + if(KHM_FAILED(kcdb_cred_create(wbuf, ident, credtype_id_krb4, &cred))) + continue; + + tt = c.issue_date + c.lifetime * 5L * 60L; + TimetToFileTime(tt, &ft); + kcdb_cred_set_attr(cred, KCDB_ATTR_EXPIRE, &ft, sizeof(ft)); + + tt = c.issue_date; + TimetToFileTime(tt, &ft); + kcdb_cred_set_attr(cred, KCDB_ATTR_ISSUE, &ft, sizeof(ft)); + + tt = c.lifetime * 5L * 60L; + TimetToFileTimeInterval(tt, &ft); + kcdb_cred_set_attr(cred, KCDB_ATTR_LIFETIME, &ft, sizeof(ft)); + + kcdb_credset_add_cred(krb4_credset, cred, -1); + + } // while + + kcdb_credset_collect(NULL, krb4_credset, ident, credtype_id_krb4, NULL); + +cleanup: + if (ptf_close == NULL) + return(KSUCCESS); + + if (open) + (*ptf_close)(); //close ticket file + + if (k_errno == EOF) + k_errno = 0; + + // XXX the if statement directly below was inserted to eliminate + // an error NO_TKT_FIL on Leash startup. The error occurs from an + // error number thrown from krb_get_tf_realm. We believe this + // change does not eliminate other errors, but it may. + + if (k_errno == NO_TKT_FIL) + k_errno = 0; + + if(ident) + kcdb_identity_release(ident); + +#if 0 + /*TODO: Handle errors here */ + if (k_errno) + { + CHAR message[256]; + CHAR errBuf[256]; + LPCSTR errText; + + if (!Lerror_message) + return -1; + + errText = err_describe(errBuf, KRBERR(k_errno)); + + sprintf(message, "%s\n\n%s failed", errText, functionName); + MessageBox(NULL, message, "Kerberos Four", + MB_OK | MB_ICONERROR | MB_TASKMODAL | MB_SETFOREGROUND); + } +#endif + return k_errno; +} + +#define KRB_FILE "KRB.CON" +#define KRBREALM_FILE "KRBREALM.CON" +#define KRB5_FILE "KRB5.INI" + +BOOL +khm_get_profile_file(LPSTR confname, UINT szConfname) +{ + char **configFile = NULL; + if (pkrb5_get_default_config_files(&configFile)) + { + GetWindowsDirectoryA(confname,szConfname); + confname[szConfname-1] = '\0'; + strncat(confname, "\\",sizeof(confname)-strlen(confname)); + confname[szConfname-1] = '\0'; + strncat(confname, KRB5_FILE,sizeof(confname)-strlen(confname)); + confname[szConfname-1] = '\0'; + return FALSE; + } + + *confname = 0; + + if (configFile) + { + strncpy(confname, *configFile, szConfname); + pkrb5_free_config_files(configFile); + } + + if (!*confname) + { + GetWindowsDirectoryA(confname,szConfname); + confname[szConfname-1] = '\0'; + strncat(confname, "\\",sizeof(confname)-strlen(confname)); + confname[szConfname-1] = '\0'; + strncat(confname, KRB5_FILE,sizeof(confname)-strlen(confname)); + confname[szConfname-1] = '\0'; + } + + return FALSE; +} + +BOOL +khm_get_krb4_con_file(LPSTR confname, UINT szConfname) +{ + if (hKrb5 && !hKrb4) + { // hold krb.con where krb5.ini is located + CHAR krbConFile[MAX_PATH]=""; + LPSTR pFind; + + //strcpy(krbConFile, CLeashApp::m_krbv5_profile->first_file->filename); + if (khm_get_profile_file(krbConFile, sizeof(krbConFile))) + { + GetWindowsDirectoryA(krbConFile,sizeof(krbConFile)); + krbConFile[MAX_PATH-1] = '\0'; + strncat(krbConFile, "\\",sizeof(krbConFile)-strlen(krbConFile)); + krbConFile[MAX_PATH-1] = '\0'; + strncat(krbConFile, KRB5_FILE,sizeof(krbConFile)-strlen(krbConFile)); + krbConFile[MAX_PATH-1] = '\0'; + } + + pFind = strrchr(krbConFile, '\\'); + if (pFind) + { + *pFind = 0; + strncat(krbConFile, "\\",sizeof(krbConFile)-strlen(krbConFile)); + krbConFile[MAX_PATH-1] = '\0'; + strncat(krbConFile, KRB_FILE,sizeof(krbConFile)-strlen(krbConFile)); + krbConFile[MAX_PATH-1] = '\0'; + } + else + krbConFile[0] = 0; + + strncpy(confname, krbConFile, szConfname); + confname[szConfname-1] = '\0'; + } + else if (hKrb4) + { + unsigned int size = szConfname; + memset(confname, '\0', szConfname); + if (!pkrb_get_krbconf2(confname, &size)) + { // Error has happened + GetWindowsDirectoryA(confname,szConfname); + confname[szConfname-1] = '\0'; + strncat(confname, "\\",szConfname-strlen(confname)); + confname[szConfname-1] = '\0'; + strncat(confname,KRB_FILE,szConfname-strlen(confname)); + confname[szConfname-1] = '\0'; + } + } + return FALSE; +} + +int +readstring(FILE * file, char * buf, int len) +{ + int c,i; + memset(buf, '\0', sizeof(buf)); + for (i=0, c=fgetc(file); c != EOF ; c=fgetc(file), i++) + { + if (i < sizeof(buf)) { + if (c == '\n') { + buf[i] = '\0'; + return i; + } else { + buf[i] = c; + } + } else { + if (c == '\n') { + buf[len-1] = '\0'; + return(i); + } + } + } + if (c == EOF) { + if (i > 0 && i < len) { + buf[i] = '\0'; + return(i); + } else { + buf[len-1] = '\0'; + return(-1); + } + } + return(-1); +} + +/*! \internal + \brief Return a list of configured realms + + The string that is returned is a set of null terminated unicode strings, + each of which denotes one realm. The set is terminated by a zero length + null terminated string. + + The caller should free the returned string using free() + + \return The string with the list of realms or NULL if the operation fails. +*/ +wchar_t * khm_krb5_get_realm_list(void) +{ + wchar_t * rlist = NULL; + + if (pprofile_get_subsection_names && pprofile_free_list) { + const char* rootSection[] = {"realms", NULL}; + const char** rootsec = rootSection; + char **sections = NULL, **cpp = NULL, *value = NULL; + + char krb5_conf[MAX_PATH+1]; + + if (!khm_get_profile_file(krb5_conf,sizeof(krb5_conf))) { + profile_t profile; + long retval; + const char *filenames[2]; + wchar_t * d; + size_t cbsize; + size_t t; + + filenames[0] = krb5_conf; + filenames[1] = NULL; + retval = pprofile_init(filenames, &profile); + if (!retval) { + retval = pprofile_get_subsection_names(profile, rootsec, §ions); + + if (!retval) + { + /* first figure out how much space to allocate */ + cbsize = 0; + for (cpp = sections; *cpp; cpp++) + { + cbsize += sizeof(wchar_t) * (strlen(*cpp) + 1); + } + cbsize += sizeof(wchar_t); /* double null terminated */ + + rlist = malloc(cbsize); + d = rlist; + for (cpp = sections; *cpp; cpp++) + { + AnsiStrToUnicode(d, cbsize, *cpp); + t = wcslen(d) + 1; + d += t; + cbsize -= sizeof(wchar_t) * t; + } + *d = L'\0'; + } + + pprofile_free_list(sections); + +#if 0 + retval = pprofile_get_string(profile, "libdefaults","noaddresses", 0, "true", &value); + if ( value ) { + disable_noaddresses = config_boolean_to_int(value); + pprofile_release_string(value); + } +#endif + pprofile_release(profile); + } + } + } else { + FILE * file; + char krb_conf[MAX_PATH+1]; + char * p; + size_t cbsize, t; + wchar_t * d; + + if (!khm_get_krb4_con_file(krb_conf,sizeof(krb_conf)) && + (file = fopen(krb_conf, "rt"))) + { + char lineBuf[256]; + + /*TODO: compute the actual required buffer size instead of hardcoding */ + cbsize = 16384; // arbitrary + rlist = malloc(cbsize); + d = rlist; + + // Skip the default realm + readstring(file,lineBuf,sizeof(lineBuf)); + + // Read the defined realms + while (TRUE) + { + if (readstring(file,lineBuf,sizeof(lineBuf)) < 0) + break; + + if (*(lineBuf + strlen(lineBuf) - 1) == '\r') + *(lineBuf + strlen(lineBuf) - 1) = 0; + + for (p=lineBuf; *p ; p++) + { + if (isspace(*p)) { + *p = 0; + break; + } + } + + if ( strncmp(".KERBEROS.OPTION.",lineBuf,17) ) { + t = strlen(lineBuf) + 1; + if(cbsize > (1 + t*sizeof(wchar_t))) { + AnsiStrToUnicode(d, cbsize, lineBuf); + d += t; + cbsize -= t * sizeof(wchar_t); + } else + break; + } + } + + *d = L'\0'; + + fclose(file); + } + } + + return rlist; +} + +/*! \internal + \brief Get the default realm + + A string will be returned that specifies the default realm. The caller + should free the string using free(). + + Returns NULL if the operation fails. +*/ +wchar_t * khm_krb5_get_default_realm(void) +{ + wchar_t * realm; + size_t cch; + krb5_context ctx=0; + char * def = 0; + + pkrb5_init_context(&ctx); + pkrb5_get_default_realm(ctx,&def); + + if (def) { + cch = strlen(def) + 1; + realm = malloc(sizeof(wchar_t) * cch); + AnsiStrToUnicode(realm, sizeof(wchar_t) * cch, def); + pkrb5_free_default_realm(ctx, def); + } else + realm = NULL; + + pkrb5_free_context(ctx); + + return realm; +} diff --git a/src/windows/identity/plugins/krb4/krb4funcs.h b/src/windows/identity/plugins/krb4/krb4funcs.h new file mode 100644 index 000000000..ea97358b9 --- /dev/null +++ b/src/windows/identity/plugins/krb4/krb4funcs.h @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/* Adapted from multiple Leash header files */ + +#ifndef __KHIMAIRA_KRB5FUNCS_H +#define __KHIMAIRA_KRB5FUNCS_H + +#include +#include + +#include +#define SECURITY_WIN32 +#include +#include + +#include + +#define LEASH_DEBUG_CLASS_GENERIC 0 +#define LEASH_DEBUG_CLASS_KRB4 1 +#define LEASH_DEBUG_CLASS_KRB4_APP 2 + +#define LEASH_PRIORITY_LOW 0 +#define LEASH_PRIORITY_HIGH 1 + +#define KRB5_DEFAULT_LIFE 60*60*10 /* 10 hours */ + +// Function Prototypes. +BOOL khm_krb5_ms2mit(BOOL); + +int +khm_krb5_kinit(krb5_context alt_ctx, + char * principal_name, + char * password, + krb5_deltat lifetime, + DWORD forwardable, + DWORD proxiable, + krb5_deltat renew_life, + DWORD addressless, + DWORD publicIP, + krb5_prompter_fct prompter, + void * p_data + ); + +long +Leash_int_kinit_ex( + krb5_context ctx, + HWND hParent, + char * principal, + char * password, + int lifetime, + int forwardable, + int proxiable, + int renew_life, + int addressless, + unsigned long publicIP, + int displayErrors + ); + +long +Leash_int_checkpwd( + char * principal, + char * password, + int displayErrors + ); + +long +Leash_int_changepwd( + char * principal, + char * password, + char * newpassword, + char** result_string, + int displayErrors + ); + +int +Leash_krb5_kdestroy( + void + ); + +int +Leash_krb5_kinit( + krb5_context, + HWND hParent, + char * principal_name, + char * password, + krb5_deltat lifetime, + DWORD forwardable, + DWORD proxiable, + krb5_deltat renew_life, + DWORD addressless, + DWORD publicIP + ); + +long +khm_convert524( + krb5_context ctx + ); + +int +Leash_afs_unlog( + void + ); + +int +Leash_afs_klog( + char *, + char *, + char *, + int + ); + +int +LeashKRB5_renew(void); + +LONG +write_registry_setting( + char* setting, + DWORD type, + void* buffer, + size_t size + ); + +LONG +read_registry_setting_user( + char* setting, + void* buffer, + size_t size + ); + +LONG +read_registry_setting( + char* setting, + void* buffer, + size_t size + ); + +BOOL +get_STRING_from_registry( + HKEY hBaseKey, + char * key, + char * value, + char * outbuf, + DWORD outlen + ); + +BOOL +get_DWORD_from_registry( + HKEY hBaseKey, + char * key, + char * value, + DWORD * result + ); + +int +config_boolean_to_int( + const char *s + ); + + +wchar_t * khm_krb5_get_default_realm(void); +wchar_t * khm_krb5_get_realm_list(void); +long khm_krb5_list_tickets(krb5_context *krbv5Context); +long khm_krb4_list_tickets(void); + + +#endif diff --git a/src/windows/identity/plugins/krb4/krb4plugin.c b/src/windows/identity/plugins/krb4/krb4plugin.c new file mode 100644 index 000000000..106febac0 --- /dev/null +++ b/src/windows/identity/plugins/krb4/krb4plugin.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include +#include + +khm_int32 credtype_id_krb4 = KCDB_CREDTYPE_INVALID; +khm_boolean krb4_initialized = FALSE; +khm_handle krb4_credset = NULL; + +/* Kerberos IV stuff */ +khm_int32 KHMAPI +krb4_msg_system(khm_int32 msg_type, khm_int32 msg_subtype, + khm_ui_4 uparam, void * vparam) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + + switch(msg_subtype) { + case KMSG_SYSTEM_INIT: + { + kcdb_credtype ct; + wchar_t buf[KCDB_MAXCCH_SHORT_DESC]; + size_t cbsize; + khui_config_node_reg reg; + wchar_t wshort_desc[KHUI_MAXCCH_SHORT_DESC]; + wchar_t wlong_desc[KHUI_MAXCCH_LONG_DESC]; + + /* perform critical registrations and initialization + stuff */ + ZeroMemory(&ct, sizeof(ct)); + ct.id = KCDB_CREDTYPE_AUTO; + ct.name = KRB4_CREDTYPE_NAME; + + if(LoadString(hResModule, IDS_KRB4_SHORT_DESC, + buf, ARRAYLENGTH(buf))) + { + StringCbLength(buf, KCDB_MAXCB_SHORT_DESC, &cbsize); + cbsize += sizeof(wchar_t); + ct.short_desc = malloc(cbsize); + StringCbCopy(ct.short_desc, cbsize, buf); + } + + /* even though ideally we should be setting limits + based KCDB_MAXCB_LONG_DESC, our long description + actually fits nicely in KCDB_MAXCB_SHORT_DESC */ + if(LoadString(hResModule, IDS_KRB4_LONG_DESC, + buf, ARRAYLENGTH(buf))) + { + StringCbLength(buf, KCDB_MAXCB_SHORT_DESC, &cbsize); + cbsize += sizeof(wchar_t); + ct.long_desc = malloc(cbsize); + StringCbCopy(ct.long_desc, cbsize, buf); + } + + ct.icon = NULL; /* TODO: set a proper icon */ + kmq_create_subscription(krb4_cb, &ct.sub); + + rv = kcdb_credtype_register(&ct, &credtype_id_krb4); + + if(KHM_SUCCEEDED(rv)) + rv = kcdb_credset_create(&krb4_credset); + + if(ct.short_desc) + free(ct.short_desc); + + if(ct.long_desc) + free(ct.long_desc); + + ZeroMemory(®, sizeof(reg)); + + reg.name = KRB4_CONFIG_NODE_NAME; + reg.short_desc = wshort_desc; + reg.long_desc = wlong_desc; + reg.h_module = hResModule; + reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_KRB4); + reg.dlg_proc = krb4_confg_proc; + reg.flags = 0; + + LoadString(hResModule, IDS_CFG_KRB4_LONG, + wlong_desc, ARRAYLENGTH(wlong_desc)); + LoadString(hResModule, IDS_CFG_KRB4_SHORT, + wshort_desc, ARRAYLENGTH(wshort_desc)); + + khui_cfg_register(NULL, ®); + + if(KHM_SUCCEEDED(rv)) { + krb4_initialized = TRUE; + + khm_krb4_list_tickets(); + } + } + break; + + case KMSG_SYSTEM_EXIT: + if(credtype_id_krb4 >= 0) + { + /* basically just unregister the credential type */ + kcdb_credtype_unregister(credtype_id_krb4); + + kcdb_credset_delete(krb4_credset); + } + break; + } + + return rv; +} + +khm_int32 KHMAPI +krb4_msg_cred(khm_int32 msg_type, khm_int32 msg_subtype, + khm_ui_4 uparam, void * vparam) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + + switch(msg_subtype) { + case KMSG_CRED_REFRESH: + { + khm_krb4_list_tickets(); + } + break; + } + + return rv; +} + +khm_int32 KHMAPI +krb4_cb(khm_int32 msg_type, khm_int32 msg_subtype, + khm_ui_4 uparam, void * vparam) +{ + switch(msg_type) { + case KMSG_SYSTEM: + return krb4_msg_system(msg_type, msg_subtype, uparam, vparam); + case KMSG_CRED: + return krb4_msg_cred(msg_type, msg_subtype, uparam, vparam); + } + return KHM_ERROR_SUCCESS; +} diff --git a/src/windows/identity/plugins/krb4/krbconfig.csv b/src/windows/identity/plugins/krb4/krbconfig.csv new file mode 100644 index 000000000..bed0d1ccb --- /dev/null +++ b/src/windows/identity/plugins/krb4/krbconfig.csv @@ -0,0 +1,23 @@ +Name,Type,Value,Description +Krb4Cred,KC_SPACE,0,"Kerberos IV Credentials Provider" + Module,KC_STRING,"MITKrb4", + Description,KC_STRING,"Kerberos IV Credentials Provider", + Dependencies,KC_STRING,Krb5Cred, + Type,KC_INT32,1, + Flags,KC_INT32,0, + Parameters,KC_SPACE,0,Parameters for KrbCred + CreateMissingConfig,KC_INT32,0,Create missing configuration files + MsLsaImport,KC_INT32,2,Automatically import MSLSA credentials + AutoRenewTickets,KC_INT32,1,Automatically renew expiring tickets + DefaultLifetime,KC_INT32,36000,Default ticket lifetime + MaxLifetime,KC_INT32,86400,Maximum lifetime + MinLifetime,KC_INT32,60,Minimum lifetime + Forwardable,KC_INT32,1,Obtain forwardable tickets (boolean) + Proxiable,KC_INT32,0,Obtain proxiable tickets (boolean) + Addressless,KC_INT32,1,Obtain addressless tickets (boolean) + Renewable,KC_INT32,1,Obtain renewable tickets (boolean) + DefaultRenewLifetime,KC_INT32,604800,Default renewable lifetime + MaxRenewLifetime,KC_INT32,2592000,Maximum renewable lifetime + MinRenewLifetime,KC_INT32,60,Maximum renewable lifetime + Parameters,KC_ENDSPACE,0, +Krb4Cred,KC_ENDSPACE,0, diff --git a/src/windows/identity/plugins/krb4/krbcred.h b/src/windows/identity/plugins/krb4/krbcred.h new file mode 100644 index 000000000..e56d114ee --- /dev/null +++ b/src/windows/identity/plugins/krb4/krbcred.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KRBAFSCRED_H +#define __KHIMAIRA_KRBAFSCRED_H + +#include + +#include +#include +#include +#include + + +#include +#include +#include +#include + +#include +#include + +#define TYPENAME_ENCTYPE L"EncType" +#define TYPENAME_ADDR_LIST L"AddrList" +#define TYPENAME_KRB5_FLAGS L"Krb5Flags" + +#define ATTRNAME_KEY_ENCTYPE L"KeyEncType" +#define ATTRNAME_TKT_ENCTYPE L"TktEncType" +#define ATTRNAME_ADDR_LIST L"AddrList" +#define ATTRNAME_KRB5_FLAGS L"Krb5Flags" +#define ATTRNAME_RENEW_TILL L"RenewTill" +#define ATTRNAME_RENEW_FOR L"RenewFor" + +void init_krb(); +void exit_krb(); +KHMEXP khm_int32 KHMAPI init_module(kmm_module h_module); +KHMEXP khm_int32 KHMAPI exit_module(kmm_module h_module); + +/* globals */ +extern kmm_module h_khModule; +extern HMODULE hResModule; +extern HINSTANCE hInstance; + +extern khm_int32 type_id_enctype; +extern khm_int32 type_id_addr_list; +extern khm_int32 type_id_krb5_flags; + +extern khm_int32 attr_id_key_enctype; +extern khm_int32 attr_id_tkt_enctype; +extern khm_int32 attr_id_addr_list; +extern khm_int32 attr_id_krb5_flags; +extern khm_int32 attr_id_renew_till; +extern khm_int32 attr_id_renew_for; + +/* Configuration spaces */ +#define CSNAME_KRB4CRED L"Krb4Cred" +#define CSNAME_PARAMS L"Parameters" + +/* plugin constants */ +#define KRB4_PLUGIN_NAME L"Krb4Cred" + +#define KRB4_PLUGIN_DEPS L"Krb5Cred\0" + +#define KRB4_CREDTYPE_NAME L"Krb4Cred" + +#define KRB4_CONFIG_NODE_NAME L"Krb4Config" + +extern khm_handle csp_plugins; +extern khm_handle csp_krbcred; +extern khm_handle csp_params; + +extern kconf_schema schema_krbconfig[]; + +/* other globals */ +extern khm_int32 credtype_id_krb4; + +extern khm_boolean krb4_initialized; + +extern khm_handle krb4_credset; + +/* plugin callbacks */ +khm_int32 KHMAPI +krb4_cb(khm_int32 msg_type, khm_int32 msg_subtype, + khm_ui_4 uparam, void * vparam); + +INT_PTR CALLBACK +krb4_confg_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); +#endif diff --git a/src/windows/identity/plugins/krb4/lang/en_us/langres.rc b/src/windows/identity/plugins/krb4/lang/en_us/langres.rc new file mode 100644 index 000000000..a5d62a26a --- /dev/null +++ b/src/windows/identity/plugins/krb4/lang/en_us/langres.rc @@ -0,0 +1,141 @@ +// Microsoft Visual C++ generated resource script. +// +#include "..\..\langres.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "..\\..\\langres.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_NC_KRB4 DIALOGEX 0, 0, 300, 166 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "kRB4",IDC_STATIC,38,43,71,24 +END + +IDD_CFG_KRB4 DIALOGEX 0, 0, 255, 182 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Ticket cache location",IDC_CFG_LBL_CACHE,7,10,67,8 + EDITTEXT IDC_CFG_CACHE,83,7,165,14,ES_AUTOHSCROLL + LTEXT "Config file path",IDC_CFG_LBL_CFGFILE,7,30,50,8 + EDITTEXT IDC_CFG_CFGPATH,83,27,113,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_CFG_CFGBROW,200,27,48,14 + LTEXT "Realm file path",IDC_CFG_LBL_RLMPATH,7,50,48,8 + EDITTEXT IDC_CFG_RLMPATH,83,47,113,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_CFG_RLMBROW,200,47,48,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_NC_KRB4, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 293 + TOPMARGIN, 7 + BOTTOMMARGIN, 159 + END + + IDD_CFG_KRB4, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 248 + VERTGUIDE, 83 + VERTGUIDE, 196 + VERTGUIDE, 200 + TOPMARGIN, 7 + BOTTOMMARGIN, 175 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_PLUGIN_DESC "Kerberos 4 Credentials Provider" +END + +STRINGTABLE +BEGIN + IDS_KRB4_SHORT_DESC "Kerberos 4 tickets" + IDS_KRB4_LONG_DESC "Kerberos 4 tickets" + IDS_CFG_KRB4_LONG "Kerberos 4 Configuration" + IDS_CFG_KRB4_SHORT "Kerberos 4" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/windows/identity/plugins/krb4/langres.h b/src/windows/identity/plugins/krb4/langres.h new file mode 100644 index 000000000..2096adec3 --- /dev/null +++ b/src/windows/identity/plugins/krb4/langres.h @@ -0,0 +1,78 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by D:\work\khimaira\src\plugins\krb4\lang\en_us\langres.rc +// +#define IDS_UNK_ADDR_FMT 101 +#define IDS_KRB5_CREDTEXT_0 102 +#define IDD_NC_KRB4 103 +#define IDS_PLUGIN_DESC 103 +#define IDS_KEY_ENCTYPE_SHORT_DESC 104 +#define IDD_CFG_KRB4 104 +#define IDS_TKT_ENCTYPE_SHORT_DESC 105 +#define IDS_KEY_ENCTYPE_LONG_DESC 106 +#define IDS_TKT_ENCTYPE_LONG_DESC 107 +#define IDS_ADDR_LIST_SHORT_DESC 108 +#define IDS_ADDR_LIST_LONG_DESC 109 +#define IDS_ETYPE_NULL 110 +#define IDS_ETYPE_DES_CBC_CRC 111 +#define IDS_ETYPE_DES_CBC_MD4 112 +#define IDS_ETYPE_DES_CBC_MD5 113 +#define IDS_ETYPE_DES_CBC_RAW 114 +#define IDS_ETYPE_DES3_CBC_SHA 115 +#define IDS_ETYPE_DES3_CBC_RAW 116 +#define IDS_ETYPE_DES_HMAC_SHA1 117 +#define IDS_ETYPE_DES3_CBC_SHA1 118 +#define IDS_ETYPE_AES128_CTS_HMAC_SHA1_96 119 +#define IDS_ETYPE_AES256_CTS_HMAC_SHA1_96 120 +#define IDS_ETYPE_ARCFOUR_HMAC 121 +#define IDS_ETYPE_ARCFOUR_HMAC_EXP 122 +#define IDS_ETYPE_UNKNOWN 123 +#define IDS_ETYPE_LOCAL_DES3_HMAC_SHA1 124 +#define IDS_ETYPE_LOCAL_RC4_MD4 125 +#define IDS_KRB5_SHORT_DESC 126 +#define IDS_KRB5_LONG_DESC 127 +#define IDS_KRB4_SHORT_DESC 128 +#define IDS_KRB4_LONG_DESC 129 +#define IDS_KRB5_FLAGS_SHORT_DESC 130 +#define IDS_RENEW_TILL_SHORT_DESC 131 +#define IDS_RENEW_TILL_LONG_DESC 132 +#define IDS_RENEW_FOR_SHORT_DESC 133 +#define IDS_RENEW_FOR_LONG_DESC 134 +#define IDS_CFG_KRB4_LONG 135 +#define IDS_CFG_KRB4_SHORT 136 +#define IDC_NCK5_RENEWABLE 1002 +#define IDC_NCK5_FORWARDABLE 1004 +#define IDC_NCK5_REALM 1005 +#define IDC_NCK5_ADD_REALMS 1006 +#define IDC_NCK5_LIFETIME_EDIT 1008 +#define IDC_NCK5_RENEW_EDIT 1009 +#define IDC_PPK5_CRENEW 1014 +#define IDC_PPK5_CFORWARD 1015 +#define IDC_PPK5_CPROXY 1016 +#define IDC_PPK5_NAME 1017 +#define IDC_PPK5_ISSUE 1018 +#define IDC_PPK5_VALID 1019 +#define IDC_PPK5_RENEW 1020 +#define IDC_CHECK2 1022 +#define IDC_CHECK4 1024 +#define IDC_PPK5_LIFETIME 1024 +#define IDC_CHECK5 1025 +#define IDC_CFG_LBL_CACHE 1025 +#define IDC_CFG_LBL_CFGFILE 1026 +#define IDC_CFG_LBL_RLMPATH 1027 +#define IDC_CFG_CACHE 1028 +#define IDC_CFG_CFGPATH 1029 +#define IDC_CFG_RLMPATH 1030 +#define IDC_CFG_CFGBROW 1031 +#define IDC_CFG_RLMBROW 1032 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 105 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1033 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/windows/identity/plugins/krb4/main.c b/src/windows/identity/plugins/krb4/main.c new file mode 100644 index 000000000..60ceb7f83 --- /dev/null +++ b/src/windows/identity/plugins/krb4/main.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include + +kmm_module h_khModule; /* KMM's handle to this module */ +HINSTANCE hInstance; +HMODULE hResModule; /* HMODULE to the resource library */ + +khm_int32 type_id_enctype = -1; +khm_int32 type_id_addr_list = -1; +khm_int32 type_id_krb5_flags = -1; + +khm_int32 attr_id_key_enctype = -1; +khm_int32 attr_id_tkt_enctype = -1; +khm_int32 attr_id_addr_list = -1; +khm_int32 attr_id_krb5_flags = -1; +khm_int32 attr_id_renew_till = -1; +khm_int32 attr_id_renew_for = -1; + +khm_handle csp_plugins = NULL; +khm_handle csp_krbcred = NULL; +khm_handle csp_params = NULL; + +kmm_module_locale locales[] = { + LOCALE_DEF(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US), L"krb4cred_en_us.dll", KMM_MLOC_FLAG_DEFAULT) +}; + +int n_locales = ARRAYLENGTH(locales); + +/* These two probably should not do anything */ +void init_krb() { +} + +void exit_krb() { +} + +/* called by the NetIDMgr module manager */ +KHMEXP khm_int32 KHMAPI init_module(kmm_module h_module) { + khm_int32 rv = KHM_ERROR_SUCCESS; + kmm_plugin_reg pi; + wchar_t buf[256]; + + h_khModule = h_module; + + rv = kmm_set_locale_info(h_module, locales, n_locales); + if(KHM_SUCCEEDED(rv)) { + hResModule = kmm_get_resource_hmodule(h_module); + } else + goto _exit; + + ZeroMemory(&pi, sizeof(pi)); + pi.name = KRB4_PLUGIN_NAME; + pi.type = KHM_PITYPE_CRED; + pi.icon = NULL; /*TODO: Assign icon */ + pi.flags = 0; + pi.msg_proc = krb4_cb; + pi.dependencies = KRB4_PLUGIN_DEPS; + pi.description = buf; + LoadString(hResModule, IDS_PLUGIN_DESC, + buf, ARRAYLENGTH(buf)); + kmm_provide_plugin(h_module, &pi); + + if(KHM_FAILED(rv = init_imports())) + goto _exit; + + if(KHM_FAILED(rv = init_error_funcs())) + goto _exit; + + /* Lookup common data types */ + if(KHM_FAILED(kcdb_type_get_id(TYPENAME_ENCTYPE, &type_id_enctype))) { + goto _exit; + } + + if(KHM_FAILED(kcdb_type_get_id(TYPENAME_ADDR_LIST, &type_id_addr_list))) { + goto _exit; + } + + if(KHM_FAILED(kcdb_type_get_id(TYPENAME_KRB5_FLAGS, &type_id_krb5_flags))) { + goto _exit; + } + + /* Lookup common attributes */ + if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_KEY_ENCTYPE, &attr_id_key_enctype))) { + goto _exit; + } + + if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_TKT_ENCTYPE, &attr_id_tkt_enctype))) { + goto _exit; + } + + if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_ADDR_LIST, &attr_id_addr_list))) { + goto _exit; + } + + if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_KRB5_FLAGS, &attr_id_krb5_flags))) { + goto _exit; + } + + if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_RENEW_TILL, &attr_id_renew_till))) { + goto _exit; + } + + if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_RENEW_FOR, &attr_id_renew_for))) { + goto _exit; + } + + rv = kmm_get_plugins_config(0, &csp_plugins); + if(KHM_FAILED(rv)) goto _exit; + + rv = khc_load_schema(csp_plugins, schema_krbconfig); + if(KHM_FAILED(rv)) goto _exit; + + rv = khc_open_space(csp_plugins, CSNAME_KRB4CRED, 0, &csp_krbcred); + if(KHM_FAILED(rv)) goto _exit; + + rv = khc_open_space(csp_krbcred, CSNAME_PARAMS, 0, &csp_params); + if(KHM_FAILED(rv)) goto _exit; + +_exit: + return rv; +} + +/* called by the NetIDMgr module manager */ +KHMEXP khm_int32 KHMAPI exit_module(kmm_module h_module) { + exit_imports(); + exit_error_funcs(); + + if(csp_params) { + khc_close_space(csp_params); + csp_params = NULL; + } + if(csp_krbcred) { + khc_close_space(csp_krbcred); + csp_krbcred = NULL; + } + if(csp_plugins) { + khc_unload_schema(csp_plugins, schema_krbconfig); + khc_close_space(csp_plugins); + csp_plugins = NULL; + } + + return KHM_ERROR_SUCCESS; /* the return code is ignored */ +} + +BOOL WINAPI DllMain( + HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved +) +{ + switch(fdwReason) { + case DLL_PROCESS_ATTACH: + hInstance = hinstDLL; + init_krb(); + break; + case DLL_PROCESS_DETACH: + exit_krb(); + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + } + + return TRUE; +} diff --git a/src/windows/identity/plugins/krb5/Makefile b/src/windows/identity/plugins/krb5/Makefile new file mode 100644 index 000000000..9bf9ef020 --- /dev/null +++ b/src/windows/identity/plugins/krb5/Makefile @@ -0,0 +1,91 @@ +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +MODULE=plugins\krb5 +!include <../../config/Makefile.w32> + +DLLFILE=$(BINDIR)\krb5cred.dll + +LIBFILE=$(LIBDIR)\krb5cred.lib + +OBJFILES= \ + $(LIBDIR)\dynimport.obj \ + $(LIBDIR)\krb5common.obj \ + $(OBJ)\main.obj \ + $(OBJ)\datarep.obj \ + $(OBJ)\errorfuncs.obj \ + $(OBJ)\krb5plugin.obj \ + $(OBJ)\krb5props.obj \ + $(OBJ)\krb5newcreds.obj \ + $(OBJ)\krb5funcs.obj \ + $(OBJ)\krb5config.obj \ + $(OBJ)\krb5identpro.obj \ + $(OBJ)\krb5configdlg.obj + +LIBFILES= \ + $(LIBDIR)\nidmgr32.lib \ + $(KFWLIBDIR)\loadfuncs.lib + +SDKLIBFILES= \ + netapi32.lib + +MSGRESFILE=$(OBJ)\krb5_msgs.res + +$(OBJ)\krb5config.c: krbconfig.csv $(CONFDIR)\csvschema.cfg + $(CCSV) $** $@ + +$(DLLFILE): $(MSGRESFILE) $(OBJFILES) + $(DLLGUILINK) $(LIBFILES) $(SDKLIBFILES) + +$(MSGRESFILE): $(OBJ)\krb5_msgs.rc + +$(OBJ)\krb5_msgs.rc: lang\krb5_msgs.mc + $(MC2RC) + +all: mkdirs $(DLLFILE) lang + +lang:: + +# Repeat this block as necessary redefining LANG for additional +# languages. + +# Begin language block +LANG=en_us + +LANGDLL=$(BINDIR)\krb5cred_$(LANG).dll + +lang:: $(LANGDLL) + +$(LANGDLL): $(OBJ)\langres_$(LANG).res + $(DLLRESLINK) + +$(OBJ)\langres_$(LANG).res: lang\$(LANG)\langres.rc + $(RC2RES) + +# End language block + +clean:: +!if defined(INCFILES) + $(RM) $(INCFILES) +!endif diff --git a/src/windows/identity/plugins/krb5/datarep.c b/src/windows/identity/plugins/krb5/datarep.c new file mode 100644 index 000000000..f8cc4cc48 --- /dev/null +++ b/src/windows/identity/plugins/krb5/datarep.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/* Data representation and related functions */ + +#include +#include +#include +#include + +khm_int32 KHMAPI enctype_toString(const void * data, khm_size cbdata, wchar_t *destbuf, khm_size *pcbdestbuf, khm_int32 flags) +{ + int resid = 0; + int etype; + wchar_t buf[256]; + size_t cblength; + + if(cbdata != sizeof(khm_int32)) + return KHM_ERROR_INVALID_PARM; + + etype = *((khm_int32 *) data); + + switch(etype) { + case ENCTYPE_NULL: + resid = IDS_ETYPE_NULL; + break; + + case ENCTYPE_DES_CBC_CRC: + resid = IDS_ETYPE_DES_CBC_CRC; + break; + + case ENCTYPE_DES_CBC_MD4: + resid = IDS_ETYPE_DES_CBC_MD4; + break; + + case ENCTYPE_DES_CBC_MD5: + resid = IDS_ETYPE_DES_CBC_MD5; + break; + + case ENCTYPE_DES_CBC_RAW: + resid = IDS_ETYPE_DES_CBC_RAW; + break; + + case ENCTYPE_DES3_CBC_SHA: + resid = IDS_ETYPE_DES3_CBC_SHA; + break; + + case ENCTYPE_DES3_CBC_RAW: + resid = IDS_ETYPE_DES3_CBC_RAW; + break; + + case ENCTYPE_DES_HMAC_SHA1: + resid = IDS_ETYPE_DES_HMAC_SHA1; + break; + + case ENCTYPE_DES3_CBC_SHA1: + resid = IDS_ETYPE_DES3_CBC_SHA1; + break; + + case ENCTYPE_AES128_CTS_HMAC_SHA1_96: + resid = IDS_ETYPE_AES128_CTS_HMAC_SHA1_96; + break; + + case ENCTYPE_AES256_CTS_HMAC_SHA1_96: + resid = IDS_ETYPE_AES256_CTS_HMAC_SHA1_96; + break; + + case ENCTYPE_ARCFOUR_HMAC: + resid = IDS_ETYPE_ARCFOUR_HMAC; + break; + + case ENCTYPE_ARCFOUR_HMAC_EXP: + resid = IDS_ETYPE_ARCFOUR_HMAC_EXP; + break; + + case ENCTYPE_UNKNOWN: + resid = IDS_ETYPE_UNKNOWN; + break; + +#if 0 + case ENCTYPE_LOCAL_DES3_HMAC_SHA1: + resid = IDS_ETYPE_LOCAL_DES3_HMAC_SHA1; + break; + + case ENCTYPE_LOCAL_RC4_MD4: + resid = IDS_ETYPE_LOCAL_RC4_MD4; + break; +#endif + } + + if(resid != 0) { + LoadString(hResModule, (UINT) resid, buf, ARRAYLENGTH(buf)); + } else { + StringCbPrintf(buf, sizeof(buf), L"#%d", etype); + } + + StringCbLength(buf, ARRAYLENGTH(buf), &cblength); + cblength += sizeof(wchar_t); + + if(!destbuf || *pcbdestbuf < cblength) { + *pcbdestbuf = cblength; + return KHM_ERROR_TOO_LONG; + } else { + StringCbCopy(destbuf, *pcbdestbuf, buf); + *pcbdestbuf = cblength; + return KHM_ERROR_SUCCESS; + } +} + +khm_int32 KHMAPI addr_list_toString(const void *d, khm_size cb_d, wchar_t *buf, khm_size *pcb_buf, khm_int32 flags) +{ + /*TODO: implement this */ + return KHM_ERROR_NOT_IMPLEMENTED; +} + +khm_int32 KHMAPI krb5flags_toString(const void *d, + khm_size cb_d, + wchar_t *buf, + khm_size *pcb_buf, + khm_int32 f) +{ + wchar_t sbuf[32]; + int i = 0; + khm_size cb; + khm_int32 flags; + + flags = *((khm_int32 *) d); + + if (flags & TKT_FLG_FORWARDABLE) + sbuf[i++] = L'F'; + + if (flags & TKT_FLG_FORWARDED) + sbuf[i++] = L'f'; + + if (flags & TKT_FLG_PROXIABLE) + sbuf[i++] = L'P'; + + if (flags & TKT_FLG_PROXY) + sbuf[i++] = L'p'; + + if (flags & TKT_FLG_MAY_POSTDATE) + sbuf[i++] = L'D'; + + if (flags & TKT_FLG_POSTDATED) + sbuf[i++] = L'd'; + + if (flags & TKT_FLG_INVALID) + sbuf[i++] = L'i'; + + if (flags & TKT_FLG_RENEWABLE) + sbuf[i++] = L'R'; + + if (flags & TKT_FLG_INITIAL) + sbuf[i++] = L'I'; + + if (flags & TKT_FLG_HW_AUTH) + sbuf[i++] = L'H'; + + if (flags & TKT_FLG_PRE_AUTH) + sbuf[i++] = L'A'; + + sbuf[i++] = L'\0'; + + cb = i * sizeof(wchar_t); + + if (!buf || *pcb_buf < cb) { + *pcb_buf = cb; + return KHM_ERROR_TOO_LONG; + } else { + StringCbCopy(buf, *pcb_buf, sbuf); + *pcb_buf = cb; + return KHM_ERROR_SUCCESS; + } +} + +khm_int32 serialize_krb5_addresses(krb5_address ** a, void ** buf, size_t * pcbbuf) +{ + /*TODO: implement this */ + return KHM_ERROR_NOT_IMPLEMENTED; +} + +#if 0 + +wchar_t * +one_addr(krb5_address *a) +{ + static wchar_t retstr[256]; + struct hostent *h; + int no_resolve = 1; + + retstr[0] = L'\0'; + + if ((a->addrtype == ADDRTYPE_INET && a->length == 4) +#ifdef AF_INET6 + || (a->addrtype == ADDRTYPE_INET6 && a->length == 16) +#endif + ) + { + int af = AF_INET; +#ifdef AF_INET6 + if (a->addrtype == ADDRTYPE_INET6) + af = AF_INET6; +#endif + if (!no_resolve) { +#ifdef HAVE_GETIPNODEBYADDR + int err; + h = getipnodebyaddr(a->contents, a->length, af, &err); + if (h) { + StringCbPrintf(retstr, sizeof(retstr), L"%S", h->h_name); + freehostent(h); + } +#else + h = gethostbyaddr(a->contents, a->length, af); + if (h) { + StringCbPrintf(retstr, sizeof(retstr), L"%S", h->h_name); + } +#endif + if (h) + return(retstr); + } + if (no_resolve || !h) { +#ifdef HAVE_INET_NTOP + char buf[46]; + const char *name = inet_ntop(a->addrtype, a->contents, buf, sizeof(buf)); + if (name) { + StringCbPrintf(retstr, sizeof(retstr), L"%S", name); + return; + } +#else + if (a->addrtype == ADDRTYPE_INET) { + StringCbPrintf(retstr, sizeof(retstr), + L"%d.%d.%d.%d", a->contents[0], a->contents[1], + a->contents[2], a->contents[3]); + return(retstr); + } +#endif + } + } + { + wchar_t tmpfmt[128]; + LoadString(hResModule, IDS_UNK_ADDR_FMT, tmpfmt, sizeof(tmpfmt)/sizeof(wchar_t)); + StringCbPrintf(retstr, sizeof(retstr), tmpfmt, a->addrtype); + } + return(retstr); +} +#endif diff --git a/src/windows/identity/plugins/krb5/datarep.h b/src/windows/identity/plugins/krb5/datarep.h new file mode 100644 index 000000000..e5388f01d --- /dev/null +++ b/src/windows/identity/plugins/krb5/datarep.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KRB_DATAREP_H +#define __KHIMAIRA_KRB_DATAREP_H + + +khm_int32 KHMAPI enctype_toString(const void * data, khm_size cbdata, wchar_t *destbuf, khm_size *pcbdestbuf, khm_int32 flags); +khm_int32 KHMAPI addr_list_toString(const void *, khm_size, wchar_t *, khm_size *, khm_int32); +khm_int32 KHMAPI krb5flags_toString(const void *, khm_size, wchar_t *, khm_size *, khm_int32); +khm_int32 KHMAPI renew_for_cb(khm_handle cred, khm_int32 id, void * buffer, khm_size * pcbsize); + + +#endif \ No newline at end of file diff --git a/src/windows/identity/plugins/krb5/errorfuncs.c b/src/windows/identity/plugins/krb5/errorfuncs.c new file mode 100644 index 000000000..ab64889cb --- /dev/null +++ b/src/windows/identity/plugins/krb5/errorfuncs.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include + +extern void (__cdecl *pinitialize_krb_error_func)(); +extern void (__cdecl *pinitialize_kadm_error_table)(); + + +khm_int32 init_error_funcs() +{ + return KHM_ERROR_SUCCESS; +} + +khm_int32 exit_error_funcs() +{ + return KHM_ERROR_SUCCESS; +} + +#ifdef DEPRECATED_REMOVABLE +HWND GetRootParent (HWND Child) +{ + HWND Last; + while (Child) + { + Last = Child; + Child = GetParent (Child); + } + return Last; +} +#endif + +void khm_err_describe(long code, wchar_t * buf, khm_size cbbuf, + DWORD * suggestion, + kherr_suggestion * suggest_code) +{ + const char * com_err_msg; + int offset; + long table_num; + DWORD msg_id = 0; + DWORD sugg_id = 0; + kherr_suggestion sugg_code = KHERR_SUGGEST_NONE; + + if (suggestion == NULL || buf == NULL || cbbuf == 0 || suggest_code == 0) + return; + + *buf = L'\0'; + + offset = (int) (code & 255); + table_num = code - offset; + com_err_msg = perror_message(code); + + *suggestion = 0; + *suggest_code = KHERR_SUGGEST_NONE; + + switch(table_num) + { + case krb_err_base: + case kadm_err_base: + break; + default: + *suggest_code = KHERR_SUGGEST_RETRY; + AnsiStrToUnicode(buf, cbbuf, com_err_msg); + return; + } + + if (table_num == krb_err_base) + switch(offset) + { + case KDC_NAME_EXP: /* 001 Principal expired */ + case KDC_SERVICE_EXP: /* 002 Service expired */ + case KDC_AUTH_EXP: /* 003 Auth expired */ + case KDC_PKT_VER: /* 004 Protocol version unknown */ + case KDC_P_MKEY_VER: /* 005 Wrong master key version */ + case KDC_S_MKEY_VER: /* 006 Wrong master key version */ + case KDC_BYTE_ORDER: /* 007 Byte order unknown */ + case KDC_PR_N_UNIQUE: /* 009 Principal not unique */ + case KDC_NULL_KEY: /* 010 Principal has null key */ + case KDC_GEN_ERR: /* 011 Generic error from KDC */ + case INTK_W_NOTALL : /* 061 Not ALL tickets returned */ + case INTK_PROT : /* 063 Protocol Error */ + case INTK_ERR : /* 070 Other error */ + msg_id = MSG_ERR_UNKNOWN; + sugg_code = KHERR_SUGGEST_RETRY; + break; + + case KDC_PR_UNKNOWN: /* 008 Principal unknown */ + msg_id = MSG_ERR_PR_UNKNOWN; + sugg_code = KHERR_SUGGEST_RETRY; + break; + case GC_TKFIL : /* 021 Can't read ticket file */ + case GC_NOTKT : /* 022 Can't find ticket or TGT */ + msg_id = MSG_ERR_TKFIL; + sugg_id = MSG_ERR_S_TKFIL; + sugg_code = KHERR_SUGGEST_RETRY; + break; + case MK_AP_TGTEXP : /* 026 TGT Expired */ + /* no extra error msg */ + break; + + case RD_AP_TIME : /* 037 delta_t too big */ + msg_id = MSG_ERR_CLOCKSKEW; + sugg_id = MSG_ERR_S_CLOCKSKEW; + sugg_code = KHERR_SUGGEST_RETRY; + break; + + case RD_AP_UNDEC : /* 031 Can't decode + authenticator */ + case RD_AP_EXP : /* 032 Ticket expired */ + case RD_AP_NYV : /* 033 Ticket not yet valid */ + case RD_AP_REPEAT : /* 034 Repeated request */ + case RD_AP_NOT_US : /* 035 The ticket isn't for us */ + case RD_AP_INCON : /* 036 Request is inconsistent */ + case RD_AP_BADD : /* 038 Incorrect net address */ + case RD_AP_VERSION : /* 039 protocol version mismatch */ + case RD_AP_MSG_TYPE : /* 040 invalid msg type */ + case RD_AP_MODIFIED : /* 041 message stream modified */ + case RD_AP_ORDER : /* 042 message out of order */ + case RD_AP_UNAUTHOR : /* 043 unauthorized request */ + /* no extra error msg */ + sugg_code = KHERR_SUGGEST_RETRY; + break; + + case GT_PW_NULL: /* 51 Current PW is null */ + case GT_PW_BADPW: /* 52 Incorrect current password */ + case GT_PW_PROT: /* 53 Protocol Error */ + case GT_PW_KDCERR: /* 54 Error returned by KDC */ + case GT_PW_NULLTKT: /* 55 Null tkt returned by KDC */ + /* no error msg yet */ + sugg_code = KHERR_SUGGEST_RETRY; + break; + + /* Values returned by send_to_kdc */ + case SKDC_RETRY : /* 56 Retry count exceeded */ + case SKDC_CANT : /* 57 Can't send request */ + msg_id = MSG_ERR_KDC_CONTACT; + break; + /* no error message on purpose: */ + case INTK_BADPW : /* 062 Incorrect password */ + sugg_code = KHERR_SUGGEST_RETRY; + break; + default: + /* no extra error msg */ + break; + } + else + switch(code) + { + case KADM_INSECURE_PW: + /* if( kadm_info != NULL ){ + * wsprintf(buf, "%s\n%s", com_err_msg, kadm_info); + * } else { + * wsprintf(buf, "%s\nPlease see the help file for information " + * "about secure passwords.", com_err_msg); + * } + * com_err_msg = buf; + */ + + /* The above code would be preferred since it allows site + * specific information to be delivered from the Kerberos + * server. However the message box is too small for VGA + * screens. It does work well if we only have to support + * 1024x768 + */ + + msg_id = MSG_ERR_INSECURE_PW; + sugg_code = KHERR_SUGGEST_RETRY; + break; + + default: + /* no extra error msg */ + break; + } + + if (msg_id != 0) { + FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | + FORMAT_MESSAGE_IGNORE_INSERTS, + KHERR_HMODULE, + msg_id, + 0, + buf, + (int) (cbbuf / sizeof(buf[0])), + NULL); + } + + if (sugg_id != 0) { + *suggestion = sugg_id; + } + + if (sugg_code != KHERR_SUGGEST_NONE) + *suggest_code = sugg_code; +} + +#ifdef DEPRECATED_REMOVABLE +int lsh_com_err_proc (LPSTR whoami, long code, + LPSTR fmt, va_list args) +{ + int retval; + HWND hOldFocus; + char buf[1024], *cp; + WORD mbformat = MB_OK | MB_ICONEXCLAMATION; + + cp = buf; + memset(buf, '\0', sizeof(buf)); + cp[0] = '\0'; + + if (code) + { + err_describe(buf, code); + while (*cp) + cp++; + } + + if (fmt) + { + if (fmt[0] == '%' && fmt[1] == 'b') + { + fmt += 2; + mbformat = va_arg(args, WORD); + /* if the first arg is a %b, we use it for the message + box MB_??? flags. */ + } + if (code) + { + *cp++ = '\n'; + *cp++ = '\n'; + } + wvsprintfA((LPSTR)cp, fmt, args); + } + hOldFocus = GetFocus(); + retval = MessageBoxA(/*GetRootParent(hOldFocus)*/NULL, buf, whoami, + mbformat | MB_ICONHAND | MB_TASKMODAL); + SetFocus(hOldFocus); + return retval; +} +#endif diff --git a/src/windows/identity/plugins/krb5/errorfuncs.h b/src/windows/identity/plugins/krb5/errorfuncs.h new file mode 100644 index 000000000..46d68f9fc --- /dev/null +++ b/src/windows/identity/plugins/krb5/errorfuncs.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_ERR_H +#define __KHIMAIRA_ERR_H + +/* All error handling and reporting related functions for the krb4/5 + and AFS plugins */ + +#include +#include +/* + * This is a hack needed because the real com_err.h does + * not define err_func. We need it in the case where + * we pull in the real com_err instead of the krb4 + * impostor. + */ +#ifndef _DCNS_MIT_COM_ERR_H +typedef LPSTR (*err_func)(int, long); +#endif + +#include +#include + +#define kadm_err_base ERROR_TABLE_BASE_kadm + +#include + +#ifndef KRBERR +#define KRBERR(code) (code + krb_err_base) +#endif + +/*! \internal + \brief Describe an error + + \param[in] code Error code returned by Kerberos + \param[out] buf Receives the error string + \param[in] cbbuf Size of buffer pointed to by \a buf + \param[out] suggestion Message ID of suggestion + \param[out] suggest_code Suggestion ID +*/ +void khm_err_describe(long code, wchar_t * buf, khm_size cbbuf, + DWORD * suggestion, + kherr_suggestion * suggest_code); + +/* */ +khm_int32 init_error_funcs(); + +khm_int32 exit_error_funcs(); + + +#endif diff --git a/src/windows/identity/plugins/krb5/krb5configdlg.c b/src/windows/identity/plugins/krb5/krb5configdlg.c new file mode 100644 index 000000000..c3b00e161 --- /dev/null +++ b/src/windows/identity/plugins/krb5/krb5configdlg.c @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include +#include +#include + +INT_PTR CALLBACK +k5_config_dlgproc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + switch(uMsg) { + case WM_INITDIALOG: + { + HWND hw; + wchar_t * realms; + wchar_t * defrealm; + wchar_t * t; + char conffile[MAX_PATH]; + wchar_t wconffile[MAX_PATH]; + wchar_t importopts[256]; + WKSTA_INFO_100 * winfo100; + + hw = GetDlgItem(hwnd, IDC_CFG_DEFREALM); +#ifdef DEBUG + assert(hw); +#endif + realms = khm_krb5_get_realm_list(); + defrealm = khm_krb5_get_default_realm(); +#ifdef DEBUG + assert(realms); + assert(defrealm); +#endif + + SendMessage(hw, CB_RESETCONTENT, 0, 0); + + for(t = realms; t && *t; t = multi_string_next(t)) { + SendMessage(hw, CB_ADDSTRING, 0, (LPARAM) t); + } + + SendMessage(hw, CB_SELECTSTRING, -1, (LPARAM) defrealm); + + free(defrealm); + free(realms); + + khm_get_profile_file(conffile, sizeof(conffile)); + + AnsiStrToUnicode(wconffile, sizeof(wconffile), conffile); + + SetDlgItemText(hwnd, IDC_CFG_CFGFILE, wconffile); + + /* hostname/domain */ + if (NetWkstaGetInfo(NULL, 100, (LPBYTE *) &winfo100) == NERR_Success) { + SetDlgItemText(hwnd, IDC_CFG_HOSTNAME, winfo100->wki100_computername); + SetDlgItemText(hwnd, IDC_CFG_DOMAIN, winfo100->wki100_langroup); + NetApiBufferFree(winfo100); + } + + /* and the import ticket options */ + LoadString(hResModule, IDS_K5CFG_IMPORT_OPTIONS, + importopts, ARRAYLENGTH(importopts)); + + hw = GetDlgItem(hwnd, IDC_CFG_IMPORT); +#ifdef DEBUG + assert(hw); +#endif + SendMessage(hw, CB_RESETCONTENT, 0, 0); + + for (t=importopts; + t && *t && *t != L' ' && + t < importopts + ARRAYLENGTH(importopts); + t = multi_string_next(t)) { + + SendMessage(hw, CB_ADDSTRING, 0, (LPARAM) t); + } + + SendMessage(hw, CB_SETCURSEL, 0, 0); + + } + break; + + case WM_DESTROY: + break; + } + return FALSE; +} + +INT_PTR CALLBACK +k5_realms_dlgproc(HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + switch(uMsg) { + case WM_INITDIALOG: + break; + + case WM_DESTROY: + break; + } + return FALSE; +} + +typedef struct tag_k5_ids_dlg_data { + khui_tracker tc_life; + khui_tracker tc_renew; + khui_tracker tc_life_min; + khui_tracker tc_life_max; + khui_tracker tc_renew_min; + khui_tracker tc_renew_max; + + time_t life; + time_t renew_life; + time_t life_min; + time_t life_max; + time_t renew_min; + time_t renew_max; +} k5_ids_dlg_data; + +static void +k5_ids_read_params(k5_ids_dlg_data * d) { + khm_int32 t; + khm_int32 rv; + +#ifdef DEBUG + assert(csp_params); +#endif + + rv = khc_read_int32(csp_params, L"DefaultLifetime", &t); + assert(KHM_SUCCEEDED(rv)); + d->life = t; + + rv = khc_read_int32(csp_params, L"DefaultRenewLifetime", &t); + assert(KHM_SUCCEEDED(rv)); + d->renew_life = t; + + rv = khc_read_int32(csp_params, L"MaxLifetime", &t); + assert(KHM_SUCCEEDED(rv)); + d->life_max = t; + + rv = khc_read_int32(csp_params, L"MinLifetime", &t); + assert(KHM_SUCCEEDED(rv)); + d->life_min = t; + + rv = khc_read_int32(csp_params, L"MaxRenewLifetime", &t); + assert(KHM_SUCCEEDED(rv)); + d->renew_max = t; + + rv = khc_read_int32(csp_params, L"MinRenewLifetime", &t); + assert(KHM_SUCCEEDED(rv)); + d->renew_min = t; + + khui_tracker_initialize(&d->tc_life); + d->tc_life.current = d->life; + d->tc_life.min = 0; + d->tc_life.max = 3600 * 24 * 7; + + khui_tracker_initialize(&d->tc_renew); + d->tc_renew.current = d->renew_life; + d->tc_renew.min = 0; + d->tc_renew.max = 3600 * 24 * 30; + + khui_tracker_initialize(&d->tc_life_min); + d->tc_life_min.current = d->life_min; + d->tc_life_min.min = d->tc_life.min; + d->tc_life_min.max = d->tc_life.max; + + khui_tracker_initialize(&d->tc_life_max); + d->tc_life_max.current = d->life_max; + d->tc_life_max.min = d->tc_life.min; + d->tc_life_max.max = d->tc_life.max; + + khui_tracker_initialize(&d->tc_renew_min); + d->tc_renew_min.current = d->renew_min; + d->tc_renew_min.min = d->tc_renew.min; + d->tc_renew_min.max = d->tc_renew.max; + + khui_tracker_initialize(&d->tc_renew_max); + d->tc_renew_max.current = d->renew_max; + d->tc_renew_max.min = d->tc_renew.min; + d->tc_renew_max.max = d->tc_renew.max; +} + +INT_PTR CALLBACK +k5_ids_tab_dlgproc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + k5_ids_dlg_data * d; + + switch(uMsg) { + case WM_INITDIALOG: + d = malloc(sizeof(*d)); +#ifdef DEBUG + assert(d); +#endif + ZeroMemory(d, sizeof(*d)); +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d); +#pragma warning(pop) + + k5_ids_read_params(d); + + khui_tracker_install(GetDlgItem(hwnd, IDC_CFG_DEFLIFE), + &d->tc_life); + khui_tracker_install(GetDlgItem(hwnd, IDC_CFG_DEFRLIFE), + &d->tc_renew); + khui_tracker_install(GetDlgItem(hwnd, IDC_CFG_LRNG_MIN), + &d->tc_life_min); + khui_tracker_install(GetDlgItem(hwnd, IDC_CFG_LRNG_MAX), + &d->tc_life_max); + khui_tracker_install(GetDlgItem(hwnd, IDC_CFG_RLRNG_MIN), + &d->tc_renew_min); + khui_tracker_install(GetDlgItem(hwnd, IDC_CFG_RLRNG_MAX), + &d->tc_renew_max); + khui_tracker_refresh(&d->tc_life); + khui_tracker_refresh(&d->tc_life_min); + khui_tracker_refresh(&d->tc_life_max); + khui_tracker_refresh(&d->tc_renew); + khui_tracker_refresh(&d->tc_renew_min); + khui_tracker_refresh(&d->tc_renew_max); + break; + + case WM_DESTROY: + d = (k5_ids_dlg_data *) (LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + + khui_tracker_kill_controls(&d->tc_life); + khui_tracker_kill_controls(&d->tc_renew); + khui_tracker_kill_controls(&d->tc_life_min); + khui_tracker_kill_controls(&d->tc_life_max); + khui_tracker_kill_controls(&d->tc_renew_min); + khui_tracker_kill_controls(&d->tc_renew_max); + break; + } + return FALSE; +} + +INT_PTR CALLBACK +k5_id_tab_dlgproc(HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + switch(uMsg) { + case WM_INITDIALOG: + break; + + case WM_DESTROY: + break; + } + return FALSE; +} + + +void +k5_register_config_panels(void) { + khui_config_node node; + khui_config_node_reg reg; + wchar_t wshort[KHUI_MAXCCH_SHORT_DESC]; + wchar_t wlong[KHUI_MAXCCH_LONG_DESC]; + + ZeroMemory(®, sizeof(reg)); + + LoadString(hResModule, IDS_K5CFG_SHORT_DESC, + wshort, ARRAYLENGTH(wshort)); + LoadString(hResModule, IDS_K5CFG_LONG_DESC, + wlong, ARRAYLENGTH(wlong)); + + reg.name = L"Kerberos5"; + reg.short_desc = wshort; + reg.long_desc = wlong; + reg.h_module = hResModule; + reg.dlg_template = MAKEINTRESOURCE(IDD_CONFIG); + reg.dlg_proc = k5_config_dlgproc; + reg.flags = 0; + + khui_cfg_register(NULL, ®); + + if (KHM_FAILED(khui_cfg_open(NULL, L"Kerberos5", &node))) { + node = NULL; +#ifdef DEBUG + assert(FALSE); +#endif + } + + ZeroMemory(®, sizeof(reg)); + + LoadString(hResModule, IDS_K5RLM_SHORT_DESC, + wshort, ARRAYLENGTH(wshort)); + LoadString(hResModule, IDS_K5RLM_LONG_DESC, + wlong, ARRAYLENGTH(wlong)); + + reg.name = L"KerberosRealms"; + reg.short_desc = wshort; + reg.long_desc = wlong; + reg.h_module = hResModule; + reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_REALMS); + reg.dlg_proc = k5_realms_dlgproc; + reg.flags = 0; + + khui_cfg_register(node, ®); + + khui_cfg_release(node); + + if (KHM_FAILED(khui_cfg_open(NULL, L"KhmIdentities", &node))) { + node = NULL; +#ifdef DEBUG + assert(FALSE); +#endif + } + + ZeroMemory(®, sizeof(reg)); + + LoadString(hResModule, IDS_K5CFG_IDS_SHORT_DESC, + wshort, ARRAYLENGTH(wshort)); + LoadString(hResModule, IDS_K5CFG_IDS_LONG_DESC, + wlong, ARRAYLENGTH(wlong)); + + reg.name = L"KerberosIdentities"; + reg.short_desc = wshort; + reg.long_desc = wlong; + reg.h_module = hResModule; + reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_IDS_TAB); + reg.dlg_proc = k5_ids_tab_dlgproc; + reg.flags = KHUI_CNFLAG_SUBPANEL; + + khui_cfg_register(node, ®); + + ZeroMemory(®, sizeof(reg)); + + LoadString(hResModule, IDS_K5CFG_ID_SHORT_DESC, + wshort, ARRAYLENGTH(wshort)); + LoadString(hResModule, IDS_K5CFG_ID_LONG_DESC, + wlong, ARRAYLENGTH(wlong)); + + reg.name = L"KerberosIdentitiesPlural"; + reg.short_desc = wshort; + reg.long_desc = wlong; + reg.h_module = hResModule; + reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_ID_TAB); + reg.dlg_proc = k5_id_tab_dlgproc; + reg.flags = KHUI_CNFLAG_SUBPANEL | KHUI_CNFLAG_PLURAL; + + khui_cfg_register(node, ®); + + khui_cfg_release(node); +} + +void +k5_unregister_config_panels(void) { + khui_config_node node_main; + khui_config_node node_realms; + khui_config_node node_ids; + khui_config_node node_tab; + + if (KHM_FAILED(khui_cfg_open(NULL, L"Kerberos5", &node_main))) { + node_main = NULL; +#ifdef DEBUG + assert(FALSE); +#endif + } + + if (KHM_SUCCEEDED(khui_cfg_open(node_main, L"KerberosRealms", + &node_realms))) { + khui_cfg_remove(node_realms); + khui_cfg_release(node_realms); + } +#ifdef DEBUG + else + assert(FALSE); +#endif + + if (node_main) { + khui_cfg_remove(node_main); + khui_cfg_release(node_main); + } + + if (KHM_FAILED(khui_cfg_open(NULL, L"KhmIdentities", &node_ids))) { + node_ids = NULL; +#ifdef DEBUG + assert(FALSE); +#endif + } + + if (KHM_SUCCEEDED(khui_cfg_open(node_ids, L"KerberosIdentities", &node_tab))) { + khui_cfg_remove(node_tab); + khui_cfg_release(node_tab); + } + if (KHM_SUCCEEDED(khui_cfg_open(node_ids, L"KerberosIdentitiesPlural", &node_tab))) { + khui_cfg_remove(node_tab); + khui_cfg_release(node_tab); + } + + if (node_ids) + khui_cfg_release(node_ids); +} diff --git a/src/windows/identity/plugins/krb5/krb5funcs.c b/src/windows/identity/plugins/krb5/krb5funcs.c new file mode 100644 index 000000000..d3c97fff2 --- /dev/null +++ b/src/windows/identity/plugins/krb5/krb5funcs.c @@ -0,0 +1,1889 @@ +/* +* Copyright (c) 2004 Massachusetts Institute of Technology +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, +* modify, merge, publish, distribute, sublicense, and/or sell copies +* of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +/* $Id$ */ + +/* Originally this was krb5routines.c in Leash sources. Subsequently +modified and adapted for NetIDMgr */ + +#include +#include + +#define SECURITY_WIN32 +#include +#include + +#include +#include +#include +#include + +long +khm_convert524(krb5_context alt_ctx) +{ + krb5_context ctx = 0; + krb5_error_code code = 0; + int icode = 0; + krb5_principal me = 0; + krb5_principal server = 0; + krb5_creds *v5creds = 0; + krb5_creds increds; + krb5_ccache cc = 0; + CREDENTIALS * v4creds = NULL; + static int init_ets = 1; + + if (!pkrb5_init_context || + !pkrb_in_tkt || + !pkrb524_init_ets || + !pkrb524_convert_creds_kdc) + return 0; + + v4creds = (CREDENTIALS *) malloc(sizeof(CREDENTIALS)); + memset((char *) v4creds, 0, sizeof(CREDENTIALS)); + + memset((char *) &increds, 0, sizeof(increds)); + /* + From this point on, we can goto cleanup because increds is + initialized. + */ + + if (alt_ctx) + { + ctx = alt_ctx; + } + else + { + code = pkrb5_init_context(&ctx); + if (code) goto cleanup; + } + + code = pkrb5_cc_default(ctx, &cc); + if (code) goto cleanup; + + if ( init_ets ) { + pkrb524_init_ets(ctx); + init_ets = 0; + } + + if (code = pkrb5_cc_get_principal(ctx, cc, &me)) + goto cleanup; + + if ((code = pkrb5_build_principal(ctx, + &server, + krb5_princ_realm(ctx, me)->length, + krb5_princ_realm(ctx, me)->data, + "krbtgt", + krb5_princ_realm(ctx, me)->data, + NULL))) + { + goto cleanup; + } + + increds.client = me; + increds.server = server; + increds.times.endtime = 0; + increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC; + if ((code = pkrb5_get_credentials(ctx, 0, + cc, + &increds, + &v5creds))) + { + goto cleanup; + } + + if ((icode = pkrb524_convert_creds_kdc(ctx, + v5creds, + v4creds))) + { + goto cleanup; + } + + /* initialize ticket cache */ + if ((icode = pkrb_in_tkt(v4creds->pname, v4creds->pinst, v4creds->realm) + != KSUCCESS)) + { + goto cleanup; + } + /* stash ticket, session key, etc. for future use */ + if ((icode = pkrb_save_credentials(v4creds->service, + v4creds->instance, + v4creds->realm, + v4creds->session, + v4creds->lifetime, + v4creds->kvno, + &(v4creds->ticket_st), + v4creds->issue_date))) + { + goto cleanup; + } + +cleanup: + memset(v4creds, 0, sizeof(v4creds)); + free(v4creds); + + if (v5creds) { + pkrb5_free_creds(ctx, v5creds); + } + if (increds.client == me) + me = 0; + if (increds.server == server) + server = 0; + pkrb5_free_cred_contents(ctx, &increds); + if (server) { + pkrb5_free_principal(ctx, server); + } + if (me) { + pkrb5_free_principal(ctx, me); + } + pkrb5_cc_close(ctx, cc); + + if (ctx && (ctx != alt_ctx)) { + pkrb5_free_context(ctx); + } + return !(code || icode); +} + +#ifdef DEPRECATED_REMOVABLE +int com_addr(void) +{ + long ipAddr; + char loc_addr[ADDR_SZ]; + CREDENTIALS cred; + char service[40]; + char instance[40]; + // char addr[40]; + char realm[40]; + struct in_addr LocAddr; + int k_errno; + + if (pkrb_get_cred == NULL) + return(KSUCCESS); + + k_errno = (*pkrb_get_cred)(service,instance,realm,&cred); + if (k_errno) + return KRBERR(k_errno); + + while(1) { + ipAddr = (*pLocalHostAddr)(); + LocAddr.s_addr = ipAddr; + StringCbCopyA(loc_addr, sizeof(loc_addr), inet_ntoa(LocAddr)); + if ( strcmp(cred.address, loc_addr) != 0) { + /* TODO: do something about this */ + //Leash_kdestroy (); + break; + } + break; + } // while() + return 0; +} +#endif + +#ifndef ENCTYPE_LOCAL_RC4_MD4 +#define ENCTYPE_LOCAL_RC4_MD4 0xFFFFFF80 +#endif + +static long get_tickets_from_cache(krb5_context ctx, + krb5_ccache cache) +{ + krb5_error_code code; + krb5_principal KRBv5Principal; + krb5_flags flags = 0; + krb5_cc_cursor KRBv5Cursor; + krb5_creds KRBv5Credentials; + krb5_ticket *tkt=NULL; + char *ClientName; + char *PrincipalName; + wchar_t wbuf[256]; /* temporary conversion buffer */ + wchar_t *wcc_name = NULL; /* credential cache name */ + char *sServerName; + khm_handle ident = NULL; + khm_handle cred = NULL; + time_t tt; + khm_int64 ft, eft; + khm_int32 ti; + + +#ifdef KRB5_TC_NOTICKET + flags = KRB5_TC_NOTICKET; +#else + flags = 0; +#endif + + { + char * cc_name; + size_t namelen; + + cc_name = (*pkrb5_cc_get_name)(ctx, cache); + if(cc_name) { + namelen = strlen(cc_name); + namelen = (namelen + 1 + 4) * sizeof(wchar_t); + /* the +4 is for the possible addtion of API: during the + cannonicalization process */ + wcc_name = malloc(namelen); + AnsiStrToUnicode(wcc_name, namelen, cc_name); + khm_krb5_canon_cc_name(wcc_name, namelen); + } + } + + if ((code = (*pkrb5_cc_set_flags)(ctx, cache, flags))) + { + if (code != KRB5_FCC_NOFILE && code != KRB5_CC_NOTFOUND) + khm_krb5_error(code, "krb5_cc_set_flags()", 0, &ctx, &cache); + + goto _exit; + } + + if ((code = (*pkrb5_cc_get_principal)(ctx, cache, &KRBv5Principal))) + { + if (code != KRB5_FCC_NOFILE && code != KRB5_CC_NOTFOUND) + khm_krb5_error(code, "krb5_cc_get_principal()", 0, &ctx, &cache); + + goto _exit; + } + + PrincipalName = NULL; + ClientName = NULL; + sServerName = NULL; + if ((code = (*pkrb5_unparse_name)(ctx, KRBv5Principal, + (char **)&PrincipalName))) + { + if (PrincipalName != NULL) + (*pkrb5_free_unparsed_name)(ctx, PrincipalName); + + (*pkrb5_free_principal)(ctx, KRBv5Principal); + + goto _exit; + } + + if (!strcspn(PrincipalName, "@" )) + { + if (PrincipalName != NULL) + (*pkrb5_free_unparsed_name)(ctx, PrincipalName); + + (*pkrb5_free_principal)(ctx, KRBv5Principal); + + goto _exit; + } + + AnsiStrToUnicode(wbuf, sizeof(wbuf), PrincipalName); + if(KHM_FAILED(kcdb_identity_create(wbuf, KCDB_IDENT_FLAG_CREATE, + &ident))) { + /* something bad happened */ + code = 1; + goto _exit; + } + + (*pkrb5_free_principal)(ctx, KRBv5Principal); + + if ((code = (*pkrb5_cc_start_seq_get)(ctx, cache, &KRBv5Cursor))) + { + goto _exit; + } + + memset(&KRBv5Credentials, '\0', sizeof(KRBv5Credentials)); + + ClientName = NULL; + sServerName = NULL; + cred = NULL; + + while (!(code = pkrb5_cc_next_cred(ctx, cache, &KRBv5Cursor, + &KRBv5Credentials))) + { + khm_handle tident = NULL; + + if(ClientName != NULL) + (*pkrb5_free_unparsed_name)(ctx, ClientName); + if(sServerName != NULL) + (*pkrb5_free_unparsed_name)(ctx, sServerName); + if(cred) + kcdb_cred_release(cred); + + ClientName = NULL; + sServerName = NULL; + cred = NULL; + + if ((*pkrb5_unparse_name)(ctx, KRBv5Credentials.client, &ClientName)) + { + (*pkrb5_free_cred_contents)(ctx, &KRBv5Credentials); + khm_krb5_error(code, "krb5_free_cred_contents()", 0, &ctx, &cache); + continue; + } + + if ((*pkrb5_unparse_name)(ctx, KRBv5Credentials.server, &sServerName)) + { + (*pkrb5_free_cred_contents)(ctx, &KRBv5Credentials); + khm_krb5_error(code, "krb5_free_cred_contents()", 0, &ctx, &cache); + continue; + } + + /* if the ClientName differs from PrincipalName for some + reason, we need to create a new identity */ + if(strcmp(ClientName, PrincipalName)) { + AnsiStrToUnicode(wbuf, sizeof(wbuf), ClientName); + if(KHM_FAILED(kcdb_identity_create(wbuf, KCDB_IDENT_FLAG_CREATE, + &tident))) { + (*pkrb5_free_cred_contents)(ctx, &KRBv5Credentials); + continue; + } + } else { + tident = ident; + } + + AnsiStrToUnicode(wbuf, sizeof(wbuf), sServerName); + if(KHM_FAILED(kcdb_cred_create(wbuf, tident, credtype_id_krb5, + &cred))) { + (*pkrb5_free_cred_contents)(ctx, &KRBv5Credentials); + continue; + } + + if (!KRBv5Credentials.times.starttime) + KRBv5Credentials.times.starttime = KRBv5Credentials.times.authtime; + + tt = KRBv5Credentials.times.starttime; + TimetToFileTime(tt, (LPFILETIME) &ft); + kcdb_cred_set_attr(cred, KCDB_ATTR_ISSUE, &ft, sizeof(ft)); + + tt = KRBv5Credentials.times.endtime; + TimetToFileTime(tt, (LPFILETIME) &eft); + kcdb_cred_set_attr(cred, KCDB_ATTR_EXPIRE, &eft, sizeof(eft)); + + eft -= ft; + kcdb_cred_set_attr(cred, KCDB_ATTR_LIFETIME, &eft, sizeof(eft)); + + if (KRBv5Credentials.times.renew_till >= 0) { + tt = KRBv5Credentials.times.renew_till; + TimetToFileTime(tt, (LPFILETIME) &eft); + kcdb_cred_set_attr(cred, KCDB_ATTR_RENEW_EXPIRE, &eft, + sizeof(eft)); + + eft -= ft; + kcdb_cred_set_attr(cred, KCDB_ATTR_RENEW_LIFETIME, &eft, + sizeof(eft)); + } + + ti = KRBv5Credentials.ticket_flags; + kcdb_cred_set_attr(cred, attr_id_krb5_flags, &ti, sizeof(ti)); + + /* special flags understood by NetIDMgr */ + { + khm_int32 oflags, nflags; + + kcdb_cred_get_flags(cred, &oflags); + nflags = oflags; + + if (ti & TKT_FLG_RENEWABLE) + nflags |= KCDB_CRED_FLAG_RENEWABLE; + if (ti & TKT_FLG_INITIAL) + nflags |= KCDB_CRED_FLAG_INITIAL; + + if (oflags != nflags) + kcdb_cred_set_flags(cred, nflags, KCDB_CRED_FLAGMASK_ALL); + } + + if ( !pkrb5_decode_ticket(&KRBv5Credentials.ticket, &tkt)) { + ti = tkt->enc_part.enctype; + kcdb_cred_set_attr(cred, attr_id_tkt_enctype, &ti, sizeof(ti)); + pkrb5_free_ticket(ctx, tkt); + tkt = NULL; + } + + ti = KRBv5Credentials.keyblock.enctype; + kcdb_cred_set_attr(cred, attr_id_key_enctype, &ti, sizeof(ti)); + + kcdb_cred_set_attr(cred, KCDB_ATTR_LOCATION, wcc_name, KCDB_CBSIZE_AUTO); + + /*TODO: going here */ +#if 0 + if ( KRBv5Credentials.addresses && KRBv5Credentials.addresses[0] ) { + int n = 0; + while ( KRBv5Credentials.addresses[n] ) + n++; + list->addrList = calloc(1, n * sizeof(char *)); + if (!list->addrList) { + MessageBox(NULL, "Memory Error", "Error", MB_OK); + return ENOMEM; + } + list->addrCount = n; + for ( n=0; naddrCount; n++ ) { + wsprintf(Buffer, "Address: %s", one_addr(KRBv5Credentials.addresses[n])); + list->addrList[n] = (char*) calloc(1, strlen(Buffer)+1); + if (!list->addrList[n]) + { + MessageBox(NULL, "Memory Error", "Error", MB_OK); + return ENOMEM; + } + strcpy(list->addrList[n], Buffer); + } + } +#endif + + if(KRBv5Credentials.ticket_flags & TKT_FLG_INITIAL) { + __int64 t_expire_old; + __int64 t_expire_new; + khm_size cb; + + /* an initial ticket! If we find one, we generally set + the lifetime, and primary ccache based on this, but + only if this initial cred has a greater lifetime than + the current primary credential. */ + + tt = KRBv5Credentials.times.endtime; + TimetToFileTime(tt, (LPFILETIME) &t_expire_new); + + cb = sizeof(t_expire_old); + if(KHM_FAILED(kcdb_identity_get_attr(tident, + KCDB_ATTR_EXPIRE, + NULL, &t_expire_old, + &cb)) + || t_expire_new > t_expire_old) + { + kcdb_identity_set_attr(tident, attr_id_krb5_ccname, + wcc_name, KCDB_CBSIZE_AUTO); + kcdb_identity_set_attr(tident, KCDB_ATTR_EXPIRE, + &t_expire_new, + sizeof(t_expire_new)); + + if (KRBv5Credentials.times.renew_till >= 0) { + tt = KRBv5Credentials.times.renew_till; + TimetToFileTime(tt, (LPFILETIME) &ft); + kcdb_identity_set_attr(tident, + KCDB_ATTR_RENEW_EXPIRE, + &ft, sizeof(ft)); + } else { + kcdb_identity_set_attr(tident, + KCDB_ATTR_RENEW_EXPIRE, + NULL, 0); + } + + ti = KRBv5Credentials.ticket_flags; + kcdb_identity_set_attr(tident, attr_id_krb5_flags, + &ti, sizeof(ti)); + } + } + + kcdb_credset_add_cred(krb5_credset, cred, -1); + + (*pkrb5_free_cred_contents)(ctx, &KRBv5Credentials); + + if(tident != ident) + kcdb_identity_release(tident); + } + + if (PrincipalName != NULL) + (*pkrb5_free_unparsed_name)(ctx, PrincipalName); + + if (ClientName != NULL) + (*pkrb5_free_unparsed_name)(ctx, ClientName); + + if (sServerName != NULL) + (*pkrb5_free_unparsed_name)(ctx, sServerName); + + if (cred) + kcdb_cred_release(cred); + + if ((code == KRB5_CC_END) || (code == KRB5_CC_NOTFOUND)) + { + if ((code = pkrb5_cc_end_seq_get(ctx, cache, &KRBv5Cursor))) + { + goto _exit; + } + + flags = KRB5_TC_OPENCLOSE; +#ifdef KRB5_TC_NOTICKET + flags |= KRB5_TC_NOTICKET; +#endif + if ((code = pkrb5_cc_set_flags(ctx, cache, flags))) + { + goto _exit; + } + } + else + { + goto _exit; + } + +_exit: + if(wcc_name) + free(wcc_name); + + return code; +} + +long +khm_krb5_list_tickets(krb5_context *krbv5Context) +{ + krb5_context ctx; + krb5_ccache cache; + krb5_error_code code; + apiCB * cc_ctx = 0; + struct _infoNC ** pNCi = NULL; + int i; + + ctx = NULL; + cache = NULL; + + kcdb_credset_flush(krb5_credset); + + code = pcc_initialize(&cc_ctx, CC_API_VER_2, NULL, NULL); + if (code) + goto _exit; + + code = pcc_get_NC_info(cc_ctx, &pNCi); + if (code) + goto _exit; + + if((*krbv5Context == 0) && (code = (*pkrb5_init_context)(krbv5Context))) { + goto _exit; + } + + ctx = (*krbv5Context); + + for(i=0; pNCi[i]; i++) { + if (pNCi[i]->vers != CC_CRED_V5) + continue; + + code = (*pkrb5_cc_resolve)(ctx, pNCi[i]->name, &cache); + + if (code) + continue; + + code = get_tickets_from_cache(ctx, cache); + + if(ctx != NULL && cache != NULL) + (*pkrb5_cc_close)(ctx, cache); + + cache = 0; + } + +_exit: + if (pNCi) + (*pcc_free_NC_info)(cc_ctx, &pNCi); + if (cc_ctx) + (*pcc_shutdown)(&cc_ctx); + + kcdb_credset_collect(NULL, krb5_credset, NULL, credtype_id_krb5, NULL); + + return(code); + +} + +int +khm_krb5_renew(khm_handle identity) +{ + krb5_error_code code = 0; + krb5_context ctx = 0; + krb5_ccache cc = 0; + krb5_principal me = 0; + krb5_principal server = 0; + krb5_creds my_creds; + krb5_data *realm = 0; + + if ( !pkrb5_init_context ) + goto cleanup; + + memset(&my_creds, 0, sizeof(krb5_creds)); + + code = khm_krb5_initialize(identity, &ctx, &cc); + if (code) + goto cleanup; + + code = pkrb5_cc_get_principal(ctx, cc, &me); + if (code) + goto cleanup; + + realm = krb5_princ_realm(ctx, me); + + code = pkrb5_build_principal_ext(ctx, &server, + realm->length,realm->data, + KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME, + realm->length,realm->data, + 0); + + if (code) + goto cleanup; + + my_creds.client = me; + my_creds.server = server; + +#ifdef KRB5_TC_NOTICKET + pkrb5_cc_set_flags(ctx, cc, 0); +#endif + code = pkrb5_get_renewed_creds(ctx, &my_creds, me, cc, NULL); +#ifdef KRB5_TC_NOTICKET + pkrb5_cc_set_flags(ctx, cc, KRB5_TC_NOTICKET); +#endif + if (code) { + if ( code != KRB5KDC_ERR_ETYPE_NOSUPP || + code != KRB5_KDC_UNREACH) + khm_krb5_error(code, "krb5_get_renewed_creds()", 0, &ctx, &cc); + goto cleanup; + } + + code = pkrb5_cc_initialize(ctx, cc, me); + if (code) goto cleanup; + + code = pkrb5_cc_store_cred(ctx, cc, &my_creds); + if (code) goto cleanup; + +cleanup: + if (my_creds.client == me) + my_creds.client = 0; + if (my_creds.server == server) + my_creds.server = 0; + + pkrb5_free_cred_contents(ctx, &my_creds); + + if (me) + pkrb5_free_principal(ctx, me); + if (server) + pkrb5_free_principal(ctx, server); + if (cc) + pkrb5_cc_close(ctx, cc); + if (ctx) + pkrb5_free_context(ctx); + return(code); +} + +int +khm_krb5_kinit(krb5_context alt_ctx, + char * principal_name, + char * password, + char * ccache, + krb5_deltat lifetime, + DWORD forwardable, + DWORD proxiable, + krb5_deltat renew_life, + DWORD addressless, + DWORD publicIP, + krb5_prompter_fct prompter, + void * p_data) +{ + krb5_error_code code = 0; + krb5_context ctx = 0; + krb5_ccache cc = 0; + krb5_principal me = 0; + char* name = 0; + krb5_creds my_creds; + krb5_get_init_creds_opt options; + krb5_address ** addrs = NULL; + int i = 0, addr_count = 0; + + if (!pkrb5_init_context) + return 0; + + pkrb5_get_init_creds_opt_init(&options); + memset(&my_creds, 0, sizeof(my_creds)); + + if (alt_ctx) + { + ctx = alt_ctx; + } + else + { + code = pkrb5_init_context(&ctx); + if (code) goto cleanup; + } + +// code = pkrb5_cc_default(ctx, &cc); + if (ccache) + code = pkrb5_cc_resolve(ctx, ccache, &cc); + else + code = pkrb5_cc_resolve(ctx, principal_name, &cc); + if (code) goto cleanup; + + code = pkrb5_parse_name(ctx, principal_name, &me); + if (code) goto cleanup; + + code = pkrb5_unparse_name(ctx, me, &name); + if (code) goto cleanup; + + if (lifetime == 0) { + khc_read_int32(csp_params, L"DefaultLifetime", &lifetime); + } + + if (lifetime) + pkrb5_get_init_creds_opt_set_tkt_life(&options, lifetime); + pkrb5_get_init_creds_opt_set_forwardable(&options, + forwardable ? 1 : 0); + pkrb5_get_init_creds_opt_set_proxiable(&options, + proxiable ? 1 : 0); + pkrb5_get_init_creds_opt_set_renew_life(&options, + renew_life); + if (addressless) + pkrb5_get_init_creds_opt_set_address_list(&options,NULL); + else { + if (publicIP) + { + // we are going to add the public IP address specified by the user + // to the list provided by the operating system + krb5_address ** local_addrs=NULL; + DWORD netIPAddr; + + pkrb5_os_localaddr(ctx, &local_addrs); + while ( local_addrs[i++] ); + addr_count = i + 1; + + addrs = (krb5_address **) malloc((addr_count+1) * sizeof(krb5_address *)); + if ( !addrs ) { + pkrb5_free_addresses(ctx, local_addrs); + assert(0); + } + memset(addrs, 0, sizeof(krb5_address *) * (addr_count+1)); + i = 0; + while ( local_addrs[i] ) { + addrs[i] = (krb5_address *)malloc(sizeof(krb5_address)); + if (addrs[i] == NULL) { + pkrb5_free_addresses(ctx, local_addrs); + assert(0); + } + + addrs[i]->magic = local_addrs[i]->magic; + addrs[i]->addrtype = local_addrs[i]->addrtype; + addrs[i]->length = local_addrs[i]->length; + addrs[i]->contents = (unsigned char *)malloc(addrs[i]->length); + if (!addrs[i]->contents) { + pkrb5_free_addresses(ctx, local_addrs); + assert(0); + } + + memcpy(addrs[i]->contents,local_addrs[i]->contents, + local_addrs[i]->length); /* safe */ + i++; + } + pkrb5_free_addresses(ctx, local_addrs); + + addrs[i] = (krb5_address *)malloc(sizeof(krb5_address)); + if (addrs[i] == NULL) + assert(0); + + addrs[i]->magic = KV5M_ADDRESS; + addrs[i]->addrtype = AF_INET; + addrs[i]->length = 4; + addrs[i]->contents = (unsigned char *)malloc(addrs[i]->length); + if (!addrs[i]->contents) + assert(0); + + netIPAddr = htonl(publicIP); + memcpy(addrs[i]->contents,&netIPAddr,4); + + pkrb5_get_init_creds_opt_set_address_list(&options,addrs); + + } + } + + code = pkrb5_get_init_creds_password(ctx, + &my_creds, + me, + password, // password + prompter, // prompter + p_data, // prompter data + 0, // start time + 0, // service name + &options); + if (code) goto cleanup; + + code = pkrb5_cc_initialize(ctx, cc, me); + if (code) goto cleanup; + + code = pkrb5_cc_store_cred(ctx, cc, &my_creds); + if (code) goto cleanup; + +cleanup: + if ( addrs ) { + for ( i=0;icontents ) + free(addrs[i]->contents); + free(addrs[i]); + } + } + } + if (my_creds.client == me) + my_creds.client = 0; + pkrb5_free_cred_contents(ctx, &my_creds); + if (name) + pkrb5_free_unparsed_name(ctx, name); + if (me) + pkrb5_free_principal(ctx, me); + if (cc) + pkrb5_cc_close(ctx, cc); + if (ctx && (ctx != alt_ctx)) + pkrb5_free_context(ctx); + return(code); +} + +long +khm_krb5_copy_ccache_by_name(krb5_context in_ctx, + wchar_t * wscc_dest, + wchar_t * wscc_src) { + krb5_context ctx = NULL; + krb5_error_code code = 0; + khm_boolean free_ctx; + krb5_ccache cc_src = NULL; + krb5_ccache cc_dest = NULL; + krb5_principal princ_src = NULL; + char scc_dest[KRB5_MAXCCH_CCNAME]; + char scc_src[KRB5_MAXCCH_CCNAME]; + int t; + + t = UnicodeStrToAnsi(scc_dest, sizeof(scc_dest), wscc_dest); + if (t == 0) + return KHM_ERROR_TOO_LONG; + t = UnicodeStrToAnsi(scc_src, sizeof(scc_src), wscc_src); + if (t == 0) + return KHM_ERROR_TOO_LONG; + + if (in_ctx) { + ctx = in_ctx; + free_ctx = FALSE; + } else { + code = pkrb5_init_context(&ctx); + if (code) { + if (ctx) + pkrb5_free_context(ctx); + return code; + } + free_ctx = TRUE; + } + + code = pkrb5_cc_resolve(ctx, scc_dest, &cc_dest); + if (code) + goto _cleanup; + + code = pkrb5_cc_resolve(ctx, scc_src, &cc_src); + if (code) + goto _cleanup; + + code = pkrb5_cc_get_principal(ctx, cc_src, &princ_src); + if (code) + goto _cleanup; + + code = pkrb5_cc_initialize(ctx, cc_dest, princ_src); + if (code) + goto _cleanup; + + code = pkrb5_cc_copy_creds(ctx, cc_src, cc_dest); + + _cleanup: + if (princ_src) + pkrb5_free_principal(ctx, princ_src); + + if (cc_dest) + pkrb5_cc_close(ctx, cc_dest); + + if (cc_src) + pkrb5_cc_close(ctx, cc_src); + + if (free_ctx && ctx) + pkrb5_free_context(ctx); + + return code; +} + +long +khm_krb5_canon_cc_name(wchar_t * wcc_name, + size_t cb_cc_name) { + size_t cb_len; + wchar_t * colon; + + if (FAILED(StringCbLength(wcc_name, + cb_cc_name, + &cb_len))) { +#ifdef DEBUG + assert(FALSE); +#else + return KHM_ERROR_TOO_LONG; +#endif + } + + cb_len += sizeof(wchar_t); + + colon = wcschr(wcc_name, L':'); + + if (colon) + return 0; + + if (cb_len + 4 * sizeof(wchar_t) > cb_cc_name) + return KHM_ERROR_TOO_LONG; + + memmove(&wcc_name[4], &wcc_name[0], cb_len); + memmove(&wcc_name[0], L"API:", sizeof(wchar_t) * 4); + + return 0; +} + +int +khm_krb5_cc_name_cmp(const wchar_t * cc_name_1, + const wchar_t * cc_name_2) { + if (!wcsncmp(cc_name_1, L"API:", 4)) + cc_name_1 += 4; + + if (!wcsncmp(cc_name_2, L"API:", 4)) + cc_name_2 += 4; + + return wcscmp(cc_name_1, cc_name_2); +} + +static khm_int32 KHMAPI +khmint_location_comp_func(khm_handle cred1, + khm_handle cred2, + void * rock) { + return kcdb_creds_comp_attr(cred1, cred2, KCDB_ATTR_LOCATION); +} + +struct khmint_location_check { + khm_handle credset; + khm_handle cred; + wchar_t * ccname; + khm_boolean success; +}; + +static khm_int32 KHMAPI +khmint_find_matching_cred_func(khm_handle cred, + void * rock) { + struct khmint_location_check * lc; + + lc = (struct khmint_location_check *) rock; + + if (!kcdb_creds_is_equal(cred, lc->cred)) + return KHM_ERROR_SUCCESS; + if (kcdb_creds_comp_attr(cred, lc->cred, KCDB_ATTR_LOCATION)) + return KHM_ERROR_SUCCESS; + + /* found it */ + lc->success = TRUE; + + /* break the search */ + return !KHM_ERROR_SUCCESS; +} + +static khm_int32 KHMAPI +khmint_location_check_func(khm_handle cred, + void * rock) { + khm_int32 t; + khm_size cb; + wchar_t ccname[KRB5_MAXCCH_CCNAME]; + struct khmint_location_check * lc; + + lc = (struct khmint_location_check *) rock; + + if (KHM_FAILED(kcdb_cred_get_type(cred, &t))) + return KHM_ERROR_SUCCESS; + + if (t != credtype_id_krb5) + return KHM_ERROR_SUCCESS; + + cb = sizeof(ccname); + if (KHM_FAILED(kcdb_cred_get_attr(cred, + KCDB_ATTR_LOCATION, + NULL, + ccname, + &cb))) + return KHM_ERROR_SUCCESS; + + if(wcscmp(ccname, lc->ccname)) + return KHM_ERROR_SUCCESS; + + lc->cred = cred; + + lc->success = FALSE; + + kcdb_credset_apply(lc->credset, + khmint_find_matching_cred_func, + (void *) lc); + + if (!lc->success) + return KHM_ERROR_NOT_FOUND; + else + return KHM_ERROR_SUCCESS; +} + +static khm_int32 KHMAPI +khmint_delete_location_func(khm_handle cred, + void * rock) { + wchar_t cc_cred[KRB5_MAXCCH_CCNAME]; + struct khmint_location_check * lc; + khm_size cb; + + lc = (struct khmint_location_check *) rock; + + cb = sizeof(cc_cred); + + if (KHM_FAILED(kcdb_cred_get_attr(cred, + KCDB_ATTR_LOCATION, + NULL, + cc_cred, + &cb))) + return KHM_ERROR_SUCCESS; + + if (wcscmp(cc_cred, lc->ccname)) + return KHM_ERROR_SUCCESS; + + kcdb_credset_del_cred_ref(lc->credset, + cred); + + return KHM_ERROR_SUCCESS; +} + +int +khm_krb5_destroy_by_credset(khm_handle p_cs) +{ + khm_handle d_cs = NULL; + khm_int32 rv = KHM_ERROR_SUCCESS; + khm_size s, cb; + krb5_context ctx; + krb5_error_code code = 0; + int i; + wchar_t ccname[KRB5_MAXCCH_CCNAME]; + struct khmint_location_check lc; + + rv = kcdb_credset_create(&d_cs); + + assert(KHM_SUCCEEDED(rv) && d_cs != NULL); + + kcdb_credset_extract(d_cs, p_cs, NULL, credtype_id_krb5); + + kcdb_credset_get_size(d_cs, &s); + + if (s == 0) { + /* nothing to do */ + kcdb_credset_delete(d_cs); + return 0; + } + + code = pkrb5_init_context(&ctx); + if (code != 0) { + rv = code; + goto _cleanup; + } + + /* we should synchronize the credential lists before we attempt to + make any assumptions on the state of the root credset */ + khm_krb5_list_tickets(&ctx); + + /* so, we need to make a decision about whether to destroy entire + ccaches or just individual credentials. Therefore we first + sort them by ccache. */ + kcdb_credset_sort(d_cs, + khmint_location_comp_func, + NULL); + + /* now, for each ccache we encounter, we check if we have all the + credentials from that ccache in the to-be-deleted list. */ + for (i=0; i < (int) s; i++) { + khm_handle cred; + + if (KHM_FAILED(kcdb_credset_get_cred(d_cs, + i, + &cred))) + continue; + + cb = sizeof(ccname); + rv = kcdb_cred_get_attr(cred, + KCDB_ATTR_LOCATION, + NULL, + ccname, + &cb); + +#ifdef DEBUG + assert(KHM_SUCCEEDED(rv)); +#endif + kcdb_cred_release(cred); + + lc.credset = d_cs; + lc.cred = NULL; + lc.ccname = ccname; + lc.success = FALSE; + + kcdb_credset_apply(NULL, + khmint_location_check_func, + (void *) &lc); + + if (lc.success) { + /* ok the destroy the ccache */ + char a_ccname[KRB5_MAXCCH_CCNAME]; + krb5_ccache cc = NULL; + + UnicodeStrToAnsi(a_ccname, + sizeof(a_ccname), + ccname); + + code = pkrb5_cc_resolve(ctx, + a_ccname, + &cc); + if (code) + goto _delete_this_set; + + code = pkrb5_cc_destroy(ctx, cc); + + if (code) { + /*TODO: report error */ + } + + _delete_this_set: + + lc.credset = d_cs; + lc.ccname = ccname; + + /* note that although we are deleting credentials off the + credential set, the size of the credential set does not + decrease since we are doing it from inside + kcdb_credset_apply(). The deleted creds will simply be + marked as deleted until kcdb_credset_purge() is + called. */ + + kcdb_credset_apply(d_cs, + khmint_delete_location_func, + (void *) &lc); + } + } + + kcdb_credset_purge(d_cs); + + /* the remainder need to be deleted one by one */ + + kcdb_credset_get_size(d_cs, &s); + + for (i=0; i < (int) s; ) { + khm_handle cred; + char a_ccname[KRB5_MAXCCH_CCNAME]; + char a_srvname[KCDB_CRED_MAXCCH_NAME]; + wchar_t srvname[KCDB_CRED_MAXCCH_NAME]; + krb5_ccache cc; + krb5_creds in_cred, out_cred; + krb5_principal princ; + khm_int32 etype; + + if (KHM_FAILED(kcdb_credset_get_cred(d_cs, + i, + &cred))) { + i++; + continue; + } + + cb = sizeof(ccname); + if (KHM_FAILED(kcdb_cred_get_attr(cred, + KCDB_ATTR_LOCATION, + NULL, + ccname, + &cb))) + goto _done_with_this_cred; + + UnicodeStrToAnsi(a_ccname, + sizeof(a_ccname), + ccname); + + code = pkrb5_cc_resolve(ctx, + a_ccname, + &cc); + + if (code) + goto _skip_similar; + + code = pkrb5_cc_get_principal(ctx, cc, &princ); + + if (code) { + pkrb5_cc_close(ctx, cc); + goto _skip_similar; + } + + _del_this_cred: + + cb = sizeof(etype); + + if (KHM_FAILED(kcdb_cred_get_attr(cred, + attr_id_key_enctype, + NULL, + &etype, + &cb))) + goto _do_next_cred; + + cb = sizeof(srvname); + if (KHM_FAILED(kcdb_cred_get_name(cred, + srvname, + &cb))) + goto _do_next_cred; + + UnicodeStrToAnsi(a_srvname, sizeof(a_srvname), srvname); + + ZeroMemory(&in_cred, sizeof(in_cred)); + + code = pkrb5_parse_name(ctx, a_srvname, &in_cred.server); + if (code) + goto _do_next_cred; + in_cred.client = princ; + in_cred.keyblock.enctype = etype; + + code = pkrb5_cc_retrieve_cred(ctx, + cc, + KRB5_TC_MATCH_SRV_NAMEONLY | + KRB5_TC_SUPPORTED_KTYPES, + &in_cred, + &out_cred); + if (code) + goto _do_next_cred_0; + + code = pkrb5_cc_remove_cred(ctx, cc, + KRB5_TC_MATCH_SRV_NAMEONLY | + KRB5_TC_SUPPORTED_KTYPES | + KRB5_TC_MATCH_AUTHDATA, + &out_cred); + + pkrb5_free_cred_contents(ctx, &out_cred); + _do_next_cred_0: + pkrb5_free_principal(ctx, in_cred.server); + _do_next_cred: + + /* check if the next cred is also of the same ccache */ + kcdb_cred_release(cred); + + for (i++; i < (int) s; i++) { + if (KHM_FAILED(kcdb_credset_get_cred(d_cs, + i, + &cred))) + continue; + } + + if (i < (int) s) { + wchar_t newcc[KRB5_MAXCCH_CCNAME]; + + cb = sizeof(newcc); + if (KHM_FAILED(kcdb_cred_get_attr(cred, + KCDB_ATTR_LOCATION, + NULL, + newcc, + &cb)) || + wcscmp(newcc, ccname)) { + i--; /* we have to look at this again */ + goto _done_with_this_set; + } + goto _del_this_cred; + } + + + _done_with_this_set: + pkrb5_free_principal(ctx, princ); + + pkrb5_cc_close(ctx, cc); + + _done_with_this_cred: + kcdb_cred_release(cred); + i++; + continue; + + _skip_similar: + kcdb_cred_release(cred); + + for (++i; i < (int) s; i++) { + wchar_t newcc[KRB5_MAXCCH_CCNAME]; + + if (KHM_FAILED(kcdb_credset_get_cred(d_cs, + i, + &cred))) + continue; + + cb = sizeof(newcc); + if (KHM_FAILED(kcdb_cred_get_attr(cred, + KCDB_ATTR_LOCATION, + NULL, + &newcc, + &cb))) { + kcdb_cred_release(cred); + continue; + } + + if (wcscmp(newcc, ccname)) { + kcdb_cred_release(cred); + break; + } + } + } + + _cleanup: + + if (d_cs) + kcdb_credset_delete(&d_cs); + + return rv; +} + +int +khm_krb5_destroy_identity(khm_handle identity) +{ + krb5_context ctx; + krb5_ccache cache; + krb5_error_code rc; + + ctx = NULL; + cache = NULL; + + if (rc = khm_krb5_initialize(identity, &ctx, &cache)) + return(rc); + + rc = pkrb5_cc_destroy(ctx, cache); + + if (ctx != NULL) + pkrb5_free_context(ctx); + + return(rc); +} + +static BOOL +GetSecurityLogonSessionData(PSECURITY_LOGON_SESSION_DATA * ppSessionData) +{ + NTSTATUS Status = 0; + HANDLE TokenHandle; + TOKEN_STATISTICS Stats; + DWORD ReqLen; + BOOL Success; + + if (!ppSessionData) + return FALSE; + *ppSessionData = NULL; + + Success = OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &TokenHandle ); + if ( !Success ) + return FALSE; + + Success = GetTokenInformation( TokenHandle, TokenStatistics, &Stats, sizeof(TOKEN_STATISTICS), &ReqLen ); + CloseHandle( TokenHandle ); + if ( !Success ) + return FALSE; + + Status = pLsaGetLogonSessionData( &Stats.AuthenticationId, ppSessionData ); + if ( FAILED(Status) || !ppSessionData ) + return FALSE; + + return TRUE; +} + +// IsKerberosLogon() does not validate whether or not there are valid +// tickets in the cache. It validates whether or not it is reasonable +// to assume that if we attempted to retrieve valid tickets we could +// do so. Microsoft does not automatically renew expired tickets. +// Therefore, the cache could contain expired or invalid tickets. +// Microsoft also caches the user's password and will use it to +// retrieve new TGTs if the cache is empty and tickets are requested. + +static BOOL +IsKerberosLogon(VOID) +{ + PSECURITY_LOGON_SESSION_DATA pSessionData = NULL; + BOOL Success = FALSE; + + if ( GetSecurityLogonSessionData(&pSessionData) ) { + if ( pSessionData->AuthenticationPackage.Buffer ) { + WCHAR buffer[256]; + WCHAR *usBuffer; + int usLength; + + Success = FALSE; + usBuffer = (pSessionData->AuthenticationPackage).Buffer; + usLength = (pSessionData->AuthenticationPackage).Length; + if (usLength < 256) + { + lstrcpynW (buffer, usBuffer, usLength); + StringCbCatW (buffer, sizeof(buffer), L""); + if ( !lstrcmpW(L"Kerberos",buffer) ) + Success = TRUE; + } + } + pLsaFreeReturnBuffer(pSessionData); + } + return Success; +} + + +BOOL +khm_krb5_ms2mit(BOOL save_creds) +{ +#ifdef NO_KRB5 + return(FALSE); +#else /* NO_KRB5 */ + krb5_context kcontext = 0; + krb5_error_code code; + krb5_ccache ccache=0; + krb5_ccache mslsa_ccache=0; + krb5_creds creds; + krb5_cc_cursor cursor=0; + krb5_principal princ = 0; + char *cache_name = NULL; + char *princ_name = NULL; + BOOL rc = FALSE; + + if ( !pkrb5_init_context ) + goto cleanup; + + if (code = pkrb5_init_context(&kcontext)) + goto cleanup; + + if (code = pkrb5_cc_resolve(kcontext, "MSLSA:", &mslsa_ccache)) + goto cleanup; + + if ( save_creds ) { + if (code = pkrb5_cc_get_principal(kcontext, mslsa_ccache, &princ)) + goto cleanup; + + if (code = pkrb5_unparse_name(kcontext, princ, &princ_name)) + goto cleanup; + + /* TODO: actually look up the preferred ccache name */ + if ((code = pkrb5_cc_resolve(kcontext, princ_name, &ccache)) || + (code = pkrb5_cc_default(kcontext, &ccache))) + goto cleanup; + + if (code = pkrb5_cc_initialize(kcontext, ccache, princ)) + goto cleanup; + + if (code = pkrb5_cc_copy_creds(kcontext, mslsa_ccache, ccache)) + goto cleanup; + + rc = TRUE; + } else { + /* Enumerate tickets from cache looking for an initial ticket */ + if ((code = pkrb5_cc_start_seq_get(kcontext, mslsa_ccache, &cursor))) + goto cleanup; + + while (!(code = pkrb5_cc_next_cred(kcontext, mslsa_ccache, &cursor, &creds))) + { + if ( creds.ticket_flags & TKT_FLG_INITIAL ) { + rc = TRUE; + pkrb5_free_cred_contents(kcontext, &creds); + break; + } + pkrb5_free_cred_contents(kcontext, &creds); + } + pkrb5_cc_end_seq_get(kcontext, mslsa_ccache, &cursor); + } + +cleanup: + if (princ_name) + pkrb5_free_unparsed_name(kcontext, princ_name); + if (princ) + pkrb5_free_principal(kcontext, princ); + if (ccache) + pkrb5_cc_close(kcontext, ccache); + if (mslsa_ccache) + pkrb5_cc_close(kcontext, mslsa_ccache); + if (kcontext) + pkrb5_free_context(kcontext); + return(rc); +#endif /* NO_KRB5 */ +} + +#define KRB_FILE "KRB.CON" +#define KRBREALM_FILE "KRBREALM.CON" +#define KRB5_FILE "KRB5.INI" + +BOOL +khm_get_profile_file(LPSTR confname, UINT szConfname) +{ + char **configFile = NULL; + if (pkrb5_get_default_config_files(&configFile)) + { + GetWindowsDirectoryA(confname,szConfname); + confname[szConfname-1] = '\0'; + strncat(confname, "\\",sizeof(confname)-strlen(confname)); + confname[szConfname-1] = '\0'; + strncat(confname, KRB5_FILE,sizeof(confname)-strlen(confname)); + confname[szConfname-1] = '\0'; + return FALSE; + } + + *confname = 0; + + if (configFile) + { + strncpy(confname, *configFile, szConfname); + pkrb5_free_config_files(configFile); + } + + if (!*confname) + { + GetWindowsDirectoryA(confname,szConfname); + confname[szConfname-1] = '\0'; + strncat(confname, "\\",sizeof(confname)-strlen(confname)); + confname[szConfname-1] = '\0'; + strncat(confname, KRB5_FILE,sizeof(confname)-strlen(confname)); + confname[szConfname-1] = '\0'; + } + + return FALSE; +} + +BOOL +khm_get_krb4_con_file(LPSTR confname, UINT szConfname) +{ + if (hKrb5 && !hKrb4) { // hold krb.con where krb5.ini is located + CHAR krbConFile[MAX_PATH]=""; + LPSTR pFind; + + //strcpy(krbConFile, CLeashApp::m_krbv5_profile->first_file->filename); + if (khm_get_profile_file(krbConFile, sizeof(krbConFile))) { + GetWindowsDirectoryA(krbConFile,sizeof(krbConFile)); + krbConFile[MAX_PATH-1] = '\0'; + strncat(krbConFile, "\\",sizeof(krbConFile)-strlen(krbConFile)); + krbConFile[MAX_PATH-1] = '\0'; + strncat(krbConFile, KRB5_FILE,sizeof(krbConFile)-strlen(krbConFile)); + krbConFile[MAX_PATH-1] = '\0'; + } + + pFind = strrchr(krbConFile, '\\'); + if (pFind) { + *pFind = 0; + strncat(krbConFile, "\\",sizeof(krbConFile)-strlen(krbConFile)); + krbConFile[MAX_PATH-1] = '\0'; + strncat(krbConFile, KRB_FILE,sizeof(krbConFile)-strlen(krbConFile)); + krbConFile[MAX_PATH-1] = '\0'; + } + else + krbConFile[0] = 0; + + strncpy(confname, krbConFile, szConfname); + confname[szConfname-1] = '\0'; + } + else if (hKrb4) { + unsigned int size = szConfname; + memset(confname, '\0', szConfname); + if (!pkrb_get_krbconf2(confname, &size)) + { // Error has happened + GetWindowsDirectoryA(confname,szConfname); + confname[szConfname-1] = '\0'; + strncat(confname, "\\",szConfname-strlen(confname)); + confname[szConfname-1] = '\0'; + strncat(confname,KRB_FILE,szConfname-strlen(confname)); + confname[szConfname-1] = '\0'; + } + } + return FALSE; +} + +int +readstring(FILE * file, char * buf, int len) +{ + int c,i; + memset(buf, '\0', sizeof(buf)); + for (i=0, c=fgetc(file); c != EOF ; c=fgetc(file), i++) { + if (i < sizeof(buf)) { + if (c == '\n') { + buf[i] = '\0'; + return i; + } else { + buf[i] = c; + } + } else { + if (c == '\n') { + buf[len-1] = '\0'; + return(i); + } + } + } + if (c == EOF) { + if (i > 0 && i < len) { + buf[i] = '\0'; + return(i); + } else { + buf[len-1] = '\0'; + return(-1); + } + } + return(-1); +} + +/*! \internal + \brief Return a list of configured realms + + The string that is returned is a set of null terminated unicode + strings, each of which denotes one realm. The set is terminated + by a zero length null terminated string. + + The caller should free the returned string using free() + + \return The string with the list of realms or NULL if the + operation fails. +*/ +wchar_t * khm_krb5_get_realm_list(void) +{ + wchar_t * rlist = NULL; + + if (pprofile_get_subsection_names && pprofile_free_list) { + const char* rootSection[] = {"realms", NULL}; + const char** rootsec = rootSection; + char **sections = NULL, **cpp = NULL, *value = NULL; + + char krb5_conf[MAX_PATH+1]; + + if (!khm_get_profile_file(krb5_conf,sizeof(krb5_conf))) { + profile_t profile; + long retval; + const char *filenames[2]; + wchar_t * d; + size_t cbsize; + size_t t; + + filenames[0] = krb5_conf; + filenames[1] = NULL; + retval = pprofile_init(filenames, &profile); + if (!retval) { + retval = pprofile_get_subsection_names(profile, rootsec, + §ions); + + if (!retval) + { + /* first figure out how much space to allocate */ + cbsize = 0; + for (cpp = sections; *cpp; cpp++) + { + cbsize += sizeof(wchar_t) * (strlen(*cpp) + 1); + } + cbsize += sizeof(wchar_t); /* double null terminated */ + + rlist = malloc(cbsize); + d = rlist; + for (cpp = sections; *cpp; cpp++) + { + AnsiStrToUnicode(d, cbsize, *cpp); + t = wcslen(d) + 1; + d += t; + cbsize -= sizeof(wchar_t) * t; + } + *d = L'\0'; + } + + pprofile_free_list(sections); + +#if 0 + retval = pprofile_get_string(profile, "libdefaults","noaddresses", 0, "true", &value); + if ( value ) { + disable_noaddresses = config_boolean_to_int(value); + pprofile_release_string(value); + } +#endif + pprofile_release(profile); + } + } + } else { + FILE * file; + char krb_conf[MAX_PATH+1]; + char * p; + size_t cbsize, t; + wchar_t * d; + + if (!khm_get_krb4_con_file(krb_conf,sizeof(krb_conf)) && + (file = fopen(krb_conf, "rt"))) + { + char lineBuf[256]; + + /*TODO: compute the actual required buffer size instead of hardcoding */ + cbsize = 16384; // arbitrary + rlist = malloc(cbsize); + d = rlist; + + // Skip the default realm + readstring(file,lineBuf,sizeof(lineBuf)); + + // Read the defined realms + while (TRUE) + { + if (readstring(file,lineBuf,sizeof(lineBuf)) < 0) + break; + + if (*(lineBuf + strlen(lineBuf) - 1) == '\r') + *(lineBuf + strlen(lineBuf) - 1) = 0; + + for (p=lineBuf; *p ; p++) + { + if (isspace(*p)) { + *p = 0; + break; + } + } + + if ( strncmp(".KERBEROS.OPTION.",lineBuf,17) ) { + t = strlen(lineBuf) + 1; + if(cbsize > (1 + t*sizeof(wchar_t))) { + AnsiStrToUnicode(d, cbsize, lineBuf); + d += t; + cbsize -= t * sizeof(wchar_t); + } else + break; + } + } + + *d = L'\0'; + + fclose(file); + } + } + + return rlist; +} + +/*! \internal + \brief Get the default realm + + A string will be returned that specifies the default realm. The + caller should free the string using free(). + + Returns NULL if the operation fails. +*/ +wchar_t * khm_krb5_get_default_realm(void) +{ + wchar_t * realm; + size_t cch; + krb5_context ctx=0; + char * def = 0; + + pkrb5_init_context(&ctx); + pkrb5_get_default_realm(ctx,&def); + + if (def) { + cch = strlen(def) + 1; + realm = malloc(sizeof(wchar_t) * cch); + AnsiStrToUnicode(realm, sizeof(wchar_t) * cch, def); + pkrb5_free_default_realm(ctx, def); + } else + realm = NULL; + + pkrb5_free_context(ctx); + + return realm; +} + +wchar_t * khm_get_realm_from_princ(wchar_t * princ) { + wchar_t * t; + + if(!princ) + return NULL; + + for (t = princ; *t; t++) { + if(*t == L'\\') { /* escape */ + t++; + if(! *t) /* malformed */ + break; + } else if (*t == L'@') + break; + } + + if (*t == '@' && *(t+1) != L'\0') + return (t+1); + else + return NULL; +} + +long +khm_krb5_changepwd(char * principal, + char * password, + char * newpassword, + char** error_str) +{ + krb5_error_code rc = 0; + int result_code; + krb5_data result_code_string, result_string; + krb5_context context = 0; + krb5_principal princ = 0; + krb5_get_init_creds_opt opts; + krb5_creds creds; + + result_string.data = 0; + result_code_string.data = 0; + + if ( !pkrb5_init_context ) + goto cleanup; + + if (rc = pkrb5_init_context(&context)) { + goto cleanup; + } + + if (rc = pkrb5_parse_name(context, principal, &princ)) { + goto cleanup; + } + + pkrb5_get_init_creds_opt_init(&opts); + pkrb5_get_init_creds_opt_set_tkt_life(&opts, 5*60); + pkrb5_get_init_creds_opt_set_renew_life(&opts, 0); + pkrb5_get_init_creds_opt_set_forwardable(&opts, 0); + pkrb5_get_init_creds_opt_set_proxiable(&opts, 0); + pkrb5_get_init_creds_opt_set_address_list(&opts,NULL); + + if (rc = pkrb5_get_init_creds_password(context, &creds, princ, + password, 0, 0, 0, + "kadmin/changepw", &opts)) { + if (rc == KRB5KRB_AP_ERR_BAD_INTEGRITY) { +#if 0 + com_err(argv[0], 0, + "Password incorrect while getting initial ticket"); +#endif + } + else { +#if 0 + com_err(argv[0], ret, "getting initial ticket"); +#endif + } + goto cleanup; + } + + if (rc = pkrb5_change_password(context, &creds, newpassword, + &result_code, &result_code_string, + &result_string)) { +#if 0 + com_err(argv[0], ret, "changing password"); +#endif + goto cleanup; + } + + if (result_code) { + int len = result_code_string.length + + (result_string.length ? (sizeof(": ") - 1) : 0) + + result_string.length; + if (len && error_str) { + *error_str = malloc(len + 1); + if (*error_str) + StringCchPrintfA(*error_str, len+1, + "%.*s%s%.*s", + result_code_string.length, + result_code_string.data, + result_string.length?": ":"", + result_string.length, + result_string.data); + } + rc = result_code; + goto cleanup; + } + + cleanup: + if (result_string.data) + pkrb5_free_data_contents(context, &result_string); + + if (result_code_string.data) + pkrb5_free_data_contents(context, &result_code_string); + + if (princ) + pkrb5_free_principal(context, princ); + + if (context) + pkrb5_free_context(context); + + return rc; +} diff --git a/src/windows/identity/plugins/krb5/krb5funcs.h b/src/windows/identity/plugins/krb5/krb5funcs.h new file mode 100644 index 000000000..79ca95646 --- /dev/null +++ b/src/windows/identity/plugins/krb5/krb5funcs.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +/* Adapted from multiple Leash header files */ + +#ifndef __KHIMAIRA_KRB5FUNCS_H +#define __KHIMAIRA_KRB5FUNCS_H + +#include +#include + +#include +#define SECURITY_WIN32 +#include +#include + +#include + +#define LEASH_DEBUG_CLASS_GENERIC 0 +#define LEASH_DEBUG_CLASS_KRB4 1 +#define LEASH_DEBUG_CLASS_KRB4_APP 2 + +#define LEASH_PRIORITY_LOW 0 +#define LEASH_PRIORITY_HIGH 1 + +#define KRB5_DEFAULT_LIFE 60*60*10 /* 10 hours */ + +#define KRB5_MAXCCH_CCNAME 1024 + +// Function Prototypes. + +BOOL +khm_krb5_ms2mit(BOOL); + +int +khm_krb5_kinit(krb5_context alt_ctx, + char * principal_name, + char * password, + char * ccache, + krb5_deltat lifetime, + DWORD forwardable, + DWORD proxiable, + krb5_deltat renew_life, + DWORD addressless, + DWORD publicIP, + krb5_prompter_fct prompter, + void * p_data); + +long +khm_krb5_changepwd(char * principal, + char * password, + char * newpassword, + char** error_str); + +int +khm_krb5_destroy_by_credset(khm_handle p_cs); + +int +khm_krb5_destroy_identity(khm_handle identity); + +long +khm_convert524(krb5_context ctx); + +int +khm_krb5_renew(khm_handle identity); + +wchar_t * +khm_krb5_get_default_realm(void); + +wchar_t * +khm_krb5_get_realm_list(void); + +long +khm_krb5_list_tickets(krb5_context *krbv5Context); + +long +khm_krb4_list_tickets(void); + +wchar_t * +khm_get_realm_from_princ(wchar_t * princ); + +long +khm_krb5_copy_ccache_by_name(krb5_context in_ctx, + wchar_t * wscc_dest, + wchar_t * wscc_src); + +long +khm_krb5_canon_cc_name(wchar_t * wcc_name, + size_t cb_cc_name); + +int +khm_krb5_cc_name_cmp(const wchar_t * cc_name_1, + const wchar_t * cc_name_2); + +BOOL +khm_get_profile_file(LPSTR confname, UINT szConfname); + +#endif diff --git a/src/windows/identity/plugins/krb5/krb5identpro.c b/src/windows/identity/plugins/krb5/krb5identpro.c new file mode 100644 index 000000000..c568e49d0 --- /dev/null +++ b/src/windows/identity/plugins/krb5/krb5identpro.c @@ -0,0 +1,1108 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include +#include + +#define K5_NCID_UN_LABEL (KHUI_CW_ID_MIN + 0) +#define K5_NCID_UN (KHUI_CW_ID_MIN + 1) +#define K5_NCID_REALM_LABEL (KHUI_CW_ID_MIN + 2) +#define K5_NCID_REALM (KHUI_CW_ID_MIN + 3) + +#define NC_UNCHANGE_TIMEOUT 3000 +#define NC_UNCHANGE_TIMER 2 +#define NC_REALMCHANGE_TIMEOUT NC_UNCHANGE_TIMEOUT +#define NC_REALMCHANGE_TIMER 3 + +typedef struct tag_k5_new_cred_data { + HWND hw_username_label; + HWND hw_username; + HWND hw_realm_label; + HWND hw_realm; +} k5_new_cred_data; + +int +k5_get_realm_from_nc(khui_new_creds * nc, + wchar_t * buf, + khm_size cch_buf) { + k5_new_cred_data * d; + + d = (k5_new_cred_data *) nc->ident_aux; + return GetWindowText(d->hw_realm, buf, (int) cch_buf); +} + +/* set the primary identity of a new credentials dialog depending on + the selection of the username and realm + + Runs in the UI thread +*/ +static void +set_identity_from_ui(khui_new_creds * nc, + k5_new_cred_data * d) { + wchar_t un[KCDB_IDENT_MAXCCH_NAME]; + wchar_t * realm; + khm_size cch; + khm_size cch_left; + khm_handle ident; + LRESULT idx = CB_ERR; + + cch = GetWindowTextLength(d->hw_username); + + /* we already set the max length of the edit control to be this. + shouldn't exceed it unless the edit control is confused. */ + assert(cch < KCDB_IDENT_MAXCCH_NAME - 1); + + GetWindowText(d->hw_username, un, ARRAYLENGTH(un)); + + realm = khm_get_realm_from_princ(un); + if (realm) /* realm was specified */ + goto _set_ident; + + /* the cch we got from GetWindowTextLength can not be trusted to + be exact. For caveats see MSDN for GetWindowTextLength. */ + StringCchLength(un, KCDB_IDENT_MAXCCH_NAME, &cch); + + realm = un + cch; /* now points at terminating NULL */ + cch_left = KCDB_IDENT_MAXCCH_NAME - cch; + + *realm++ = L'@'; + cch_left--; + + cch = GetWindowTextLength(d->hw_realm); + if (cch == 0 || cch >= cch_left) + goto _set_null_ident; + + GetWindowText(d->hw_realm, realm, (int) cch_left); + + _set_ident: + if (KHM_FAILED(kcdb_identity_create(un, + KCDB_IDENT_FLAG_CREATE, + &ident))) + goto _set_null_ident; + + khui_cw_set_primary_id(nc, ident); + + kcdb_identity_release(ident); + return; + + _set_null_ident: + khui_cw_set_primary_id(nc, NULL); + return; +} + +static BOOL +update_crossfeed(khui_new_creds * nc, + k5_new_cred_data * d, + int ctrl_id_src) { + wchar_t un[KCDB_IDENT_MAXCCH_NAME]; + wchar_t * un_realm; + wchar_t realm[KCDB_IDENT_MAXCCH_NAME]; + khm_size cch; + khm_size cch_left; + + cch = (khm_size) GetWindowTextLength(d->hw_username); +#ifdef DEBUG + assert(cch < KCDB_IDENT_MAXCCH_NAME); +#endif + if (cch == 0) + return FALSE; + + GetWindowText(d->hw_username, + un, + ARRAYLENGTH(un)); + + un_realm = khm_get_realm_from_princ(un); + + if (un_realm == NULL) + return FALSE; + + if (ctrl_id_src == K5_NCID_UN) { + SendMessage(d->hw_realm, + CB_SELECTSTRING, + (WPARAM) -1, + (LPARAM) un_realm); + + SetWindowText(d->hw_realm, + un_realm); + + return TRUE; + } + /* else... */ + + cch_left = KCDB_IDENT_MAXCCH_NAME - (un_realm - un); + + cch = (khm_size) GetWindowTextLength(d->hw_realm); + +#ifdef DEBUG + assert(cch < KCDB_IDENT_MAXCCH_NAME); +#endif + if (cch == 0) + return FALSE; + + GetWindowText(d->hw_realm, realm, + ARRAYLENGTH(realm)); + + StringCchCopy(un_realm, cch_left, realm); + + SendMessage(d->hw_username, + CB_SELECTSTRING, + (WPARAM) -1, + (LPARAM) un); + + SetWindowText(d->hw_username, un); + + return TRUE; +} + +/* Handle window messages for the identity specifiers + + runs in UI thread */ +static LRESULT +handle_wnd_msg(khui_new_creds * nc, + HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + k5_new_cred_data * d; + + d = (k5_new_cred_data *) nc->ident_aux; + + switch(uMsg) { + case WM_COMMAND: + switch(wParam) { + case MAKEWPARAM(K5_NCID_UN, CBN_EDITCHANGE): + /* the username has changed. Instead of handling this + for every keystroke, set a timer that elapses some + time afterwards and then handle the event. */ + SetTimer(hwnd, NC_UNCHANGE_TIMER, + NC_UNCHANGE_TIMEOUT, NULL); + return TRUE; + + case MAKEWPARAM(K5_NCID_UN, CBN_KILLFOCUS): + case MAKEWPARAM(K5_NCID_UN, CBN_CLOSEUP): + KillTimer(hwnd, NC_UNCHANGE_TIMER); + + update_crossfeed(nc,d,K5_NCID_UN); + set_identity_from_ui(nc,d); + return TRUE; + + case MAKEWPARAM(K5_NCID_REALM,CBN_EDITCHANGE): + SetTimer(hwnd, NC_REALMCHANGE_TIMER, + NC_REALMCHANGE_TIMEOUT, NULL); + return TRUE; + + case MAKEWPARAM(K5_NCID_REALM,CBN_KILLFOCUS): + case MAKEWPARAM(K5_NCID_REALM,CBN_CLOSEUP): + KillTimer(hwnd, NC_REALMCHANGE_TIMER); + + update_crossfeed(nc,d,K5_NCID_REALM); + set_identity_from_ui(nc, d); + return TRUE; + } + break; + + case WM_TIMER: + if(wParam == NC_UNCHANGE_TIMER) { + KillTimer(hwnd, NC_UNCHANGE_TIMER); + + update_crossfeed(nc, d, K5_NCID_UN); + set_identity_from_ui(nc,d); + return TRUE; + } else if (wParam == NC_REALMCHANGE_TIMER) { + KillTimer(hwnd, NC_REALMCHANGE_TIMER); + + update_crossfeed(nc, d, K5_NCID_REALM); + set_identity_from_ui(nc, d); + return TRUE; + } + break; + } + return FALSE; +} + +/* UI Callback + + runs in UI thread */ +static LRESULT KHMAPI +ui_cb(khui_new_creds * nc, + UINT cmd, + HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + k5_new_cred_data * d; + + d = (k5_new_cred_data *) nc->ident_aux; + + switch(cmd) { + case WMNC_IDENT_INIT: + { + wchar_t defident[KCDB_IDENT_MAXCCH_NAME]; + wchar_t wbuf[1024]; + wchar_t * ms = NULL; + wchar_t * t; + wchar_t * defrealm = NULL; + LRESULT lr; + khm_size cb_ms; + khm_size cb; + HWND hw_parent; + khm_int32 rv; + khm_handle hident; + + hw_parent = (HWND) lParam; + defident[0] = L'\0'; + +#ifdef DEBUG + assert(d == NULL); + assert(hw_parent != NULL); +#endif + + d = malloc(sizeof(*d)); + assert(d); + ZeroMemory(d, sizeof(*d)); + + khui_cw_lock_nc(nc); + nc->ident_aux = (LPARAM) d; + khui_cw_unlock_nc(nc); + + LoadString(hResModule, IDS_NC_USERNAME, + wbuf, ARRAYLENGTH(wbuf)); + + d->hw_username_label = CreateWindow + (L"STATIC", + wbuf, + SS_SIMPLE | WS_CHILD | WS_VISIBLE, + 0, 0, 100, 100, /* bogus values */ + hw_parent, + (HMENU) K5_NCID_UN_LABEL, + hInstance, + NULL); + assert(d->hw_username_label != NULL); + + d->hw_username = CreateWindow + (L"COMBOBOX", + L"", + CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | + WS_CHILD | WS_VISIBLE | WS_TABSTOP, + 0, 0, 100, 100, /* bogus values */ + hw_parent, + (HMENU) K5_NCID_UN, + hInstance, + NULL); + assert(d->hw_username != NULL); + + SendMessage(d->hw_username, + CB_LIMITTEXT, + (WPARAM)(KCDB_IDENT_MAXCCH_NAME - 1), + 0); + + SendMessage(d->hw_username, + CB_SETEXTENDEDUI, + (WPARAM) TRUE, + 0); + + khui_cw_add_control_row(nc, + d->hw_username_label, + d->hw_username, + KHUI_CTRLSIZE_SMALL); + + LoadString(hResModule, IDS_NC_REALM, + wbuf, ARRAYLENGTH(wbuf)); + + d->hw_realm_label = CreateWindow + (L"STATIC", + wbuf, + SS_SIMPLE | WS_CHILD | WS_VISIBLE, + 0, 0, 100, 100, /* bogus */ + hw_parent, + (HMENU) K5_NCID_REALM_LABEL, + hInstance, + NULL); + assert(d->hw_realm_label != NULL); + + d->hw_realm = CreateWindow + (L"COMBOBOX", + L"", + CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | + WS_CHILD | WS_VISIBLE | WS_TABSTOP, + 0, 0, 100, 100, /* bogus */ + hw_parent, + (HMENU) K5_NCID_REALM, + hInstance, + NULL); + assert(d->hw_realm != NULL); + + SendMessage(d->hw_realm, + CB_LIMITTEXT, + (WPARAM) (KCDB_IDENT_MAXCCH_NAME - 1), + 0); + + SendMessage(d->hw_realm, + CB_SETEXTENDEDUI, + (WPARAM) TRUE, + 0); + + khui_cw_add_control_row(nc, + d->hw_realm_label, + d->hw_realm, + KHUI_CTRLSIZE_SMALL); + + /* add the LRU realms and principals to the dropdown + lists */ + rv = khc_read_multi_string(csp_params, + L"LRUPrincipals", + NULL, + &cb_ms); + + if (rv != KHM_ERROR_TOO_LONG) + goto _add_lru_realms; + + ms = malloc(cb_ms); + assert(ms != NULL); + + cb = cb_ms; + rv = khc_read_multi_string(csp_params, + L"LRUPrincipals", + ms, + &cb); + + assert(KHM_SUCCEEDED(rv)); + + /* the first of these is considered the default identity + if no other default is known */ + StringCbCopy(defident, sizeof(defident), ms); + + t = ms; + while(t && *t) { + SendMessage(d->hw_username, + CB_ADDSTRING, + 0, + (LPARAM) t); + + t = multi_string_next(t); + } + + _add_lru_realms: + /* add the default realm first */ + defrealm = khm_krb5_get_default_realm(); + if (defrealm) { + SendMessage(d->hw_realm, + CB_ADDSTRING, + 0, + (LPARAM) defrealm); + } + + rv = khc_read_multi_string(csp_params, + L"LRURealms", + NULL, + &cb); + + if (rv != KHM_ERROR_TOO_LONG) + goto _done_adding_lru; + + if (ms != NULL) { + if (cb_ms < cb) { + free(ms); + ms = malloc(cb); + assert(ms); + cb_ms = cb; + } + } else { + ms = malloc(cb); + cb_ms = cb; + } + + rv = khc_read_multi_string(csp_params, + L"LRURealms", + ms, + &cb); + + assert(KHM_SUCCEEDED(rv)); + + for (t = ms; t && *t; t = multi_string_next(t)) { + lr = SendMessage(d->hw_realm, + CB_FINDSTRINGEXACT, + (WPARAM) -1, + (LPARAM) t); + if (lr != CB_ERR) + continue; + + SendMessage(d->hw_realm, + CB_ADDSTRING, + 0, + (LPARAM) t); + } + + _done_adding_lru: + /* set the current selection of the realms list */ + if (defrealm) { + SendMessage(d->hw_realm, + CB_SELECTSTRING, + (WPARAM) -1, + (LPARAM) defrealm); + } else { + SendMessage(d->hw_realm, + CB_SETCURSEL, + (WPARAM) 0, + (LPARAM) 0); + } + + if (defrealm) + free(defrealm); + + if (ms) + free(ms); + + /* now see about that default identity */ + if (nc->ctx.identity) { + cb = sizeof(defident); + kcdb_identity_get_name(nc->ctx.identity, + defident, + &cb); + } + + if (defident[0] == L'\0' && + KHM_SUCCEEDED(kcdb_identity_get_default(&hident))) { + cb = sizeof(defident); + kcdb_identity_get_name(hident, defident, &cb); + kcdb_identity_release(hident); + } + + if (defident[0] == L'\0') { + DWORD dw; + + dw = ARRAYLENGTH(defident); + GetUserName(defident, &dw); + } + + t = khm_get_realm_from_princ(defident); + if (t) { + /* there is a realm */ + assert(t != defident); + *--t = L'\0'; + t++; + + SendMessage(d->hw_realm, + CB_SELECTSTRING, + (WPARAM) -1, + (LPARAM) t); + + SendMessage(d->hw_realm, + WM_SETTEXT, + 0, + (LPARAM) t); + } + + if (defident[0] != L'\0') { + /* there is a username */ + SendMessage(d->hw_username, + CB_SELECTSTRING, + (WPARAM) -1, + (LPARAM) defident); + + SendMessage(d->hw_username, + WM_SETTEXT, + 0, + (LPARAM) defident); + } + + set_identity_from_ui(nc, d); + } + return TRUE; + + case WMNC_IDENT_WMSG: + return handle_wnd_msg(nc, hwnd, uMsg, wParam, lParam); + + case WMNC_IDENT_EXIT: + { +#ifdef DEBUG + assert(d != NULL); +#endif + khui_cw_lock_nc(nc); + nc->ident_aux = 0; + khui_cw_unlock_nc(nc); + + /* since we created all the windows as child windows of + the new creds window, they will be destroyed when that + window is destroyed. */ + free(d); + } + return TRUE; + } + return FALSE; +} + +static khm_int32 +k5_ident_valiate_name(khm_int32 msg_type, + khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam) { + krb5_principal princ = NULL; + char princ_name[KCDB_IDENT_MAXCCH_NAME]; + kcdb_ident_name_xfer * nx; + krb5_error_code code; + + nx = (kcdb_ident_name_xfer *) vparam; + + if(UnicodeStrToAnsi(princ_name, sizeof(princ_name), + nx->name_src) == 0) { + nx->result = KHM_ERROR_INVALID_NAME; + return KHM_ERROR_SUCCESS; + } + + assert(k5_identpro_ctx != NULL); + + code = pkrb5_parse_name(k5_identpro_ctx, + princ_name, + &princ); + + if (code) { + nx->result = KHM_ERROR_INVALID_NAME; + return KHM_ERROR_SUCCESS; + } + + if (princ != NULL) + pkrb5_free_principal(k5_identpro_ctx, + princ); + + nx->result = KHM_ERROR_SUCCESS; + + return KHM_ERROR_SUCCESS; +} + +static khm_int32 +k5_ident_set_default(khm_int32 msg_type, + khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam) { + + /* Logic for setting the default identity: + + When setting identity I as the default; + + - If KRB5CCNAME is set + - If I["Krb5CCName"] == %KRB5CCNAME% + - do nothing + - Else + - Copy the contents of I["Krb5CCName"] to %KRB5CCNAME + - Set I["Krb5CCName"] to %KRB5CCNAME + - Else + - Set HKCU\Software\MIT\kerberos5,ccname to + "API:".I["Krb5CCName"] + */ + + if (uparam) { + /* an identity is being made default */ + khm_handle def_ident = (khm_handle) vparam; + wchar_t env_ccname[KRB5_MAXCCH_CCNAME]; + wchar_t id_ccname[KRB5_MAXCCH_CCNAME]; + khm_size cb; + DWORD dw; + LONG l; + +#ifdef DEBUG + assert(def_ident != NULL); +#endif + + cb = sizeof(id_ccname); + if (KHM_FAILED(kcdb_identity_get_attr(def_ident, + attr_id_krb5_ccname, + NULL, + id_ccname, + &cb))) + return KHM_ERROR_UNKNOWN; + + khm_krb5_canon_cc_name(id_ccname, sizeof(id_ccname)); + + StringCbLength(id_ccname, sizeof(id_ccname), &cb); + cb += sizeof(wchar_t); + + dw = GetEnvironmentVariable(L"KRB5CCNAME", + env_ccname, + ARRAYLENGTH(env_ccname)); + + if (dw == 0 && + GetLastError() == ERROR_ENVVAR_NOT_FOUND) { + /* KRB5CCNAME not set */ + HKEY hk_ccname; + DWORD dwType; + DWORD dwSize; + wchar_t reg_ccname[KRB5_MAXCCH_CCNAME]; + + l = RegOpenKeyEx(HKEY_CURRENT_USER, + L"Software\\MIT\\kerberos5", + 0, + KEY_READ | KEY_WRITE, + &hk_ccname); + + if (l != ERROR_SUCCESS) + l = RegCreateKeyEx(HKEY_CURRENT_USER, + L"Software\\MIT\\kerberos5", + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_READ | KEY_WRITE, + NULL, + &hk_ccname, + &dw); + + if (l != ERROR_SUCCESS) + return KHM_ERROR_UNKNOWN; + + dwSize = sizeof(reg_ccname); + + l = RegQueryValueEx(hk_ccname, + L"ccname", + NULL, + &dwType, + (LPBYTE) reg_ccname, + &dwSize); + + if (l != ERROR_SUCCESS || + dwType != REG_SZ || + khm_krb5_cc_name_cmp(reg_ccname, id_ccname)) { + + /* we have to write the new value in */ + + l = RegSetValueEx(hk_ccname, + L"ccname", + 0, + REG_SZ, + (BYTE *) id_ccname, + (DWORD) cb); + } + + RegCloseKey(hk_ccname); + + if (l == ERROR_SUCCESS) + return KHM_ERROR_SUCCESS; + else + return KHM_ERROR_UNKNOWN; + + } else if (dw > ARRAYLENGTH(env_ccname)) { + /* buffer was not enough */ +#ifdef DEBUG + assert(FALSE); +#else + return KHM_ERROR_UNKNOWN; +#endif + } else { + /* KRB5CCNAME is set */ + long code; + krb5_context ctx; + + /* if the %KRB5CCNAME is the same as the identity + ccache, then it is already the default. */ + if (!khm_krb5_cc_name_cmp(id_ccname, env_ccname)) + return KHM_ERROR_SUCCESS; + + /* if not, we have to copy the contents of id_ccname + to env_ccname */ + code = pkrb5_init_context(&ctx); + if (code) + return KHM_ERROR_UNKNOWN; + + code = khm_krb5_copy_ccache_by_name(ctx, + env_ccname, + id_ccname); + + if (code == 0) + khm_krb5_list_tickets(&ctx); + + if (ctx) + pkrb5_free_context(ctx); + + return (code == 0)?KHM_ERROR_SUCCESS:KHM_ERROR_UNKNOWN; + } + } else { + /* the default identity is being forgotten */ + + /* we don't really do anything about this case */ + } + + return KHM_ERROR_SUCCESS; +} + +static khm_int32 +k5_ident_get_ui_cb(khm_int32 msg_type, + khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam) { + khui_ident_new_creds_cb * cb; + + cb = (khui_ident_new_creds_cb *) vparam; + + *cb = ui_cb; + + return KHM_ERROR_SUCCESS; +} + +static khm_int32 +k5_ident_notify_create(khm_int32 msg_type, + khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam) { + + /* a new identity has been created. What we want to do at + this point is to check if the identity belongs to krb5 + and to see if it is the default. */ + + krb5_ccache cc = NULL; + krb5_error_code code; + krb5_principal princ = NULL; + char * princ_nameA = NULL; + wchar_t princ_nameW[KCDB_IDENT_MAXCCH_NAME]; + wchar_t id_nameW[KCDB_IDENT_MAXCCH_NAME]; + khm_size cb; + khm_handle ident; + + ident = (khm_handle) vparam; + + assert(k5_identpro_ctx != NULL); + + code = pkrb5_cc_default(k5_identpro_ctx, &cc); + if (code) + goto _nc_cleanup; + + code = pkrb5_cc_get_principal(k5_identpro_ctx, + cc, + &princ); + if (code) + goto _nc_cleanup; + + code = pkrb5_unparse_name(k5_identpro_ctx, + princ, + &princ_nameA); + if (code) + goto _nc_cleanup; + + AnsiStrToUnicode(princ_nameW, + sizeof(princ_nameW), + princ_nameA); + + cb = sizeof(id_nameW); + + if (KHM_FAILED(kcdb_identity_get_name(ident, + id_nameW, + &cb))) + goto _nc_cleanup; + + if (!wcscmp(id_nameW, princ_nameW)) { + kcdb_identity_set_default_int(ident); + } + + _nc_cleanup: + if (princ_nameA) + pkrb5_free_unparsed_name(k5_identpro_ctx, + princ_nameA); + if (princ) + pkrb5_free_principal(k5_identpro_ctx, + princ); + if (cc) + pkrb5_cc_close(k5_identpro_ctx, cc); + + + return KHM_ERROR_SUCCESS; +} + +static khm_int32 KHMAPI +k5_ident_update_apply_proc(khm_handle cred, + void * rock) { + wchar_t ccname[KRB5_MAXCCH_CCNAME]; + khm_handle tident = (khm_handle) rock; + khm_handle ident = NULL; + khm_int32 t; + khm_int32 flags; + __int64 t_expire; + __int64 t_rexpire; + khm_size cb; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if (KHM_FAILED(kcdb_cred_get_type(cred, &t)) || + t != credtype_id_krb5 || + KHM_FAILED(kcdb_cred_get_identity(cred, &ident))) + return KHM_ERROR_SUCCESS; + + if (ident != tident) + goto _cleanup; + + if (KHM_FAILED(kcdb_cred_get_flags(cred, &flags))) + flags = 0; + + cb = sizeof(t_expire); + if (KHM_SUCCEEDED(kcdb_cred_get_attr(cred, + KCDB_ATTR_EXPIRE, + NULL, + &t_expire, + &cb))) { + __int64 t_cexpire; + + cb = sizeof(t_cexpire); + if ((flags & KCDB_CRED_FLAG_INITIAL) || + KHM_FAILED(kcdb_identity_get_attr(tident, + KCDB_ATTR_EXPIRE, + NULL, + &t_cexpire, + &cb)) || + t_cexpire > t_expire) + kcdb_identity_set_attr(tident, KCDB_ATTR_EXPIRE, + &t_expire, sizeof(t_expire)); + } else if (flags & KCDB_CRED_FLAG_INITIAL) { + kcdb_identity_set_attr(tident, KCDB_ATTR_EXPIRE, NULL, 0); + } + + cb = sizeof(ccname); + if (KHM_SUCCEEDED(kcdb_cred_get_attr(cred, KCDB_ATTR_LOCATION, + NULL, + ccname, + &cb))) { + kcdb_identity_set_attr(tident, attr_id_krb5_ccname, + ccname, cb); + } else { + kcdb_identity_set_attr(tident, attr_id_krb5_ccname, + NULL, 0); + } + + if (!(flags & KCDB_CRED_FLAG_INITIAL)) + goto _cleanup; + + cb = sizeof(t); + if (KHM_SUCCEEDED(kcdb_cred_get_attr(cred, + attr_id_krb5_flags, + NULL, + &t, + &cb))) { + kcdb_identity_set_attr(tident, attr_id_krb5_flags, + &t, sizeof(t)); + + cb = sizeof(t_rexpire); + if (!(t & TKT_FLG_RENEWABLE) || + KHM_FAILED(kcdb_cred_get_attr(cred, + KCDB_ATTR_RENEW_EXPIRE, + NULL, + &t_rexpire, + &cb))) { + kcdb_identity_set_attr(tident, KCDB_ATTR_RENEW_EXPIRE, + NULL, 0); + } else { + kcdb_identity_set_attr(tident, KCDB_ATTR_RENEW_EXPIRE, + &t_rexpire, sizeof(t_rexpire)); + } + } else { + kcdb_identity_set_attr(tident, attr_id_krb5_flags, + NULL, 0); + kcdb_identity_set_attr(tident, KCDB_ATTR_RENEW_EXPIRE, + NULL, 0); + } + + rv = KHM_ERROR_EXIT; + + _cleanup: + if (ident) + kcdb_identity_release(ident); + + return rv; +} + +static khm_int32 +k5_ident_update(khm_int32 msg_type, + khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam) { + + khm_handle ident; + + ident = (khm_handle) vparam; + if (ident == NULL) + return KHM_ERROR_SUCCESS; + + kcdb_credset_apply(NULL, + k5_ident_update_apply_proc, + (void *) ident); + + return KHM_ERROR_SUCCESS; +} + + +static khm_int32 +k5_ident_init(khm_int32 msg_type, + khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam) { + /* just like notify_create, except now we set the default identity + based on what we find in the configuration */ + krb5_ccache cc = NULL; + krb5_error_code code; + krb5_principal princ = NULL; + char * princ_nameA = NULL; + wchar_t princ_nameW[KCDB_IDENT_MAXCCH_NAME]; + khm_handle ident = NULL; + + assert(k5_identpro_ctx != NULL); + + code = pkrb5_cc_default(k5_identpro_ctx, &cc); + if (code) + goto _nc_cleanup; + + code = pkrb5_cc_get_principal(k5_identpro_ctx, + cc, + &princ); + if (code) + goto _nc_cleanup; + + code = pkrb5_unparse_name(k5_identpro_ctx, + princ, + &princ_nameA); + if (code) + goto _nc_cleanup; + + AnsiStrToUnicode(princ_nameW, + sizeof(princ_nameW), + princ_nameA); + + if (KHM_FAILED(kcdb_identity_create(princ_nameW, + 0, + &ident))) + goto _nc_cleanup; + + kcdb_identity_set_default_int(ident); + + _nc_cleanup: + if (princ_nameA) + pkrb5_free_unparsed_name(k5_identpro_ctx, + princ_nameA); + if (princ) + pkrb5_free_principal(k5_identpro_ctx, + princ); + if (cc) + pkrb5_cc_close(k5_identpro_ctx, cc); + + if (ident) + kcdb_identity_release(ident); + + return KHM_ERROR_SUCCESS; +} + +static khm_int32 +k5_ident_exit(khm_int32 msg_type, + khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam) { + /* don't really do anything */ + return KHM_ERROR_SUCCESS; +} + +#if 0 +/* copy and paste template for ident provider messages */ +static khm_int32 +k5_ident_(khm_int32 msg_type, + khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam) { +} +#endif + +khm_int32 KHMAPI +k5_msg_ident(khm_int32 msg_type, + khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam) +{ + switch(msg_subtype) { + case KMSG_IDENT_INIT: + return k5_ident_init(msg_type, + msg_subtype, + uparam, + vparam); + + case KMSG_IDENT_EXIT: + return k5_ident_exit(msg_type, + msg_subtype, + uparam, + vparam); + + case KMSG_IDENT_VALIDATE_NAME: + return k5_ident_valiate_name(msg_type, + msg_subtype, + uparam, + vparam); + + case KMSG_IDENT_VALIDATE_IDENTITY: + /* TODO: handle KMSG_IDENT_VALIDATE_IDENTITY */ + break; + + case KMSG_IDENT_CANON_NAME: + /* TODO: handle KMSG_IDENT_CANON_NAME */ + break; + + case KMSG_IDENT_COMPARE_NAME: + /* TODO: handle KMSG_IDENT_COMPARE_NAME */ + break; + + case KMSG_IDENT_SET_DEFAULT: + return k5_ident_set_default(msg_type, + msg_subtype, + uparam, + vparam); + + case KMSG_IDENT_SET_SEARCHABLE: + /* TODO: handle KMSG_IDENT_SET_SEARCHABLE */ + break; + + case KMSG_IDENT_GET_INFO: + /* TODO: handle KMSG_IDENT_GET_INFO */ + break; + + case KMSG_IDENT_UPDATE: + return k5_ident_update(msg_type, + msg_subtype, + uparam, + vparam); + + case KMSG_IDENT_ENUM_KNOWN: + /* TODO: handle KMSG_IDENT_ENUM_KNOWN */ + break; + + case KMSG_IDENT_GET_UI_CALLBACK: + return k5_ident_get_ui_cb(msg_type, + msg_subtype, + uparam, + vparam); + + case KMSG_IDENT_NOTIFY_CREATE: + return k5_ident_notify_create(msg_type, + msg_subtype, + uparam, + vparam); + } + + return KHM_ERROR_SUCCESS; +} diff --git a/src/windows/identity/plugins/krb5/krb5newcreds.c b/src/windows/identity/plugins/krb5/krb5newcreds.c new file mode 100644 index 000000000..968e0e290 --- /dev/null +++ b/src/windows/identity/plugins/krb5/krb5newcreds.c @@ -0,0 +1,2167 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include + +#include + +extern LPVOID k5_main_fiber; +extern LPVOID k5_kinit_fiber; + +typedef struct k5_dlg_data_t { + khui_new_creds * nc; + + khui_tracker tc_lifetime; + khui_tracker tc_renew; + + BOOL dirty; + + DWORD renewable; + DWORD forwardable; + DWORD proxiable; + DWORD addressless; + DWORD publicIP; + + wchar_t * cred_message; /* overrides the credential text, if + non-NULL */ +} k5_dlg_data; + + +INT_PTR +k5_handle_wm_initdialog(HWND hwnd, + WPARAM wParam, + LPARAM lParam) +{ + HWND hw; + k5_dlg_data * d; + khui_new_creds_by_type * nct; + + d = malloc(sizeof(*d)); + ZeroMemory(d, sizeof(*d)); + /* lParam is a pointer to a khui_new_creds structure */ + d->nc = (khui_new_creds *) lParam; + khui_cw_find_type(d->nc, credtype_id_krb5, &nct); + +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, DWLP_USER, (LPARAM) d); +#pragma warning(pop) + + nct->aux = (LPARAM) d; + + if (d->nc->subtype == KMSG_CRED_NEW_CREDS) { + khui_tracker_initialize(&d->tc_lifetime); + khui_tracker_initialize(&d->tc_renew); + + hw = GetDlgItem(hwnd, IDC_NCK5_LIFETIME_EDIT); + khui_tracker_install(hw, &d->tc_lifetime); + + hw = GetDlgItem(hwnd, IDC_NCK5_RENEW_EDIT); + khui_tracker_install(hw, &d->tc_renew); + } + return TRUE; +} + +INT_PTR +k5_handle_wm_destroy(HWND hwnd, + WPARAM wParam, + LPARAM lParam) +{ + k5_dlg_data * d; + khui_new_creds_by_type * nct = NULL; + + d = (k5_dlg_data *) (LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); +#ifdef DEBUG + assert(d); +#endif + + khui_cw_find_type(d->nc, credtype_id_krb5, &nct); + +#ifdef DEBUG + assert(nct); +#endif + + nct->aux = 0; + + if (d->nc->subtype == KMSG_CRED_NEW_CREDS) { + khui_tracker_kill_controls(&d->tc_renew); + khui_tracker_kill_controls(&d->tc_lifetime); + } + + free(d); + + return TRUE; +} + +INT_PTR +k5_handle_wmnc_notify(HWND hwnd, + WPARAM wParam, + LPARAM lParam) +{ + switch(HIWORD(wParam)) { + case WMNC_DIALOG_MOVE: + { + k5_dlg_data * d; + + d = (k5_dlg_data *)(LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + + if (d->nc->subtype == KMSG_CRED_NEW_CREDS) { + khui_tracker_reposition(&d->tc_lifetime); + khui_tracker_reposition(&d->tc_renew); + } + + return TRUE; + } + break; + + case WMNC_DIALOG_SETUP: + { + k5_dlg_data * d; + + d = (k5_dlg_data *)(LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + + if (d->nc->subtype == KMSG_CRED_PASSWORD) + return TRUE; + + /* need to update the controls with d->* */ + if(d->renewable) { + SendDlgItemMessage(hwnd, IDC_NCK5_RENEWABLE, + BM_SETCHECK, BST_CHECKED, + 0); + EnableWindow(GetDlgItem(hwnd, IDC_NCK5_RENEW_EDIT), + TRUE); + } else { + SendDlgItemMessage(hwnd, IDC_NCK5_RENEWABLE, + BM_SETCHECK, BST_UNCHECKED, 0); + EnableWindow(GetDlgItem(hwnd, IDC_NCK5_RENEW_EDIT), + FALSE); + } + + khui_tracker_refresh(&d->tc_lifetime); + khui_tracker_refresh(&d->tc_renew); + + if(d->forwardable) { + SendDlgItemMessage(hwnd, IDC_NCK5_FORWARDABLE, + BM_SETCHECK, BST_CHECKED, 0); + } else { + SendDlgItemMessage(hwnd, IDC_NCK5_FORWARDABLE, + BM_SETCHECK, BST_UNCHECKED, 0); + } + } + break; + + case WMNC_UPDATE_CREDTEXT: + { + k5_dlg_data * d; + khui_new_creds * nc; + khui_new_creds_by_type * nct; + wchar_t sbuf[1024]; + wchar_t fbuf[256]; + wchar_t tbuf[256]; + size_t cbsize; + khm_int32 flags; + + d = (k5_dlg_data *)(LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + nc = d->nc; + khui_cw_find_type(nc, credtype_id_krb5, &nct); + + if(nct == NULL) + break; + + if(nct->credtext) + free(nct->credtext); + nct->credtext = NULL; + + tbuf[0] = L'\0'; + + if (nc->n_identities > 0 && + KHM_SUCCEEDED(kcdb_identity_get_flags(nc->identities[0], + &flags)) && + (flags & KCDB_IDENT_FLAG_VALID) && + nc->subtype == KMSG_CRED_NEW_CREDS) { + + if (is_k5_identpro) + k5_get_realm_from_nc(nc, tbuf, ARRAYLENGTH(tbuf)); + else + GetDlgItemText(hwnd, IDC_NCK5_REALM, tbuf, + ARRAYLENGTH(tbuf)); + + /*TODO: if additional realms were specified, then those + must be listed as well */ + LoadString(hResModule, IDS_KRB5_CREDTEXT_0, + fbuf, ARRAYLENGTH(fbuf)); + StringCbPrintf(sbuf, sizeof(sbuf), fbuf, + tbuf); + + StringCbLength(sbuf, sizeof(sbuf), &cbsize); + cbsize += sizeof(wchar_t); + + nct->credtext = malloc(cbsize); + + StringCbCopy(nct->credtext, cbsize, sbuf); + } else if (nc->n_identities > 0 && + nc->subtype == KMSG_CRED_PASSWORD) { + cbsize = sizeof(tbuf); + kcdb_identity_get_name(nc->identities[0], tbuf, &cbsize); + + LoadString(hResModule, IDS_KRB5_CREDTEXT_P0, + fbuf, ARRAYLENGTH(fbuf)); + StringCbPrintf(sbuf, sizeof(sbuf), fbuf, tbuf); + + StringCbLength(sbuf, sizeof(sbuf), &cbsize); + cbsize += sizeof(wchar_t); + + nct->credtext = malloc(cbsize); + + StringCbCopy(nct->credtext, cbsize, sbuf); + } else { + if (d->cred_message) { + StringCbLength(d->cred_message, KHUI_MAXCB_BANNER, + &cbsize); + cbsize += sizeof(wchar_t); + + nct->credtext = malloc(cbsize); + + StringCbCopy(nct->credtext, cbsize, d->cred_message); + } + } + } + break; + + case WMNC_IDENTITY_CHANGE: + { + /* There has been a change of identity */ + k5_dlg_data * d; + + d = (k5_dlg_data *)(LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + + kmq_post_sub_msg(k5_sub, KMSG_CRED, + KMSG_CRED_DIALOG_NEW_IDENTITY, + 0, (void *) d->nc); + } + break; + + case WMNC_DIALOG_PREPROCESS: + { + k5_dlg_data * d; + + d = (k5_dlg_data *)(LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + + if(d->dirty) { + kmq_post_sub_msg(k5_sub, KMSG_CRED, + KMSG_CRED_DIALOG_NEW_OPTIONS, + 0, (void *) d->nc); + + /* the above notification effectively takes + all our changes into account. The data we + have is no longer dirty */ + d->dirty = FALSE; + } + } + break; + } + + return 0; +} + +INT_PTR +k5_handle_wm_command(HWND hwnd, + WPARAM wParam, + LPARAM lParam) +{ + int cid; + int notif; + k5_dlg_data * d; + + d = (k5_dlg_data *)(LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER); + + cid = LOWORD(wParam); + notif = HIWORD(wParam); + + if(notif == BN_CLICKED && cid == IDC_NCK5_RENEWABLE) { + int c; + c = (int) SendDlgItemMessage(hwnd, IDC_NCK5_RENEWABLE, + BM_GETCHECK, 0, 0); + if(c==BST_CHECKED) { + EnableWindow(GetDlgItem(hwnd, IDC_NCK5_RENEW_EDIT), TRUE); + d->renewable = TRUE; + } else { + EnableWindow(GetDlgItem(hwnd, IDC_NCK5_RENEW_EDIT), FALSE); + d->renewable = FALSE; + } + d->dirty = TRUE; + } else if(notif == BN_CLICKED && cid == IDC_NCK5_FORWARDABLE) { + int c; + c = (int) SendDlgItemMessage(hwnd, IDC_NCK5_RENEWABLE, + BM_GETCHECK, 0, 0); + if(c==BST_CHECKED) { + d->forwardable = TRUE; + } else { + d->forwardable = FALSE; + } + d->dirty = TRUE; + } else if((notif == CBN_SELCHANGE || + notif == CBN_KILLFOCUS) && + cid == IDC_NCK5_REALM && + !is_k5_identpro) { + /* find out what the realm of the current identity + is, and if they are the same, then we don't do + anything */ + wchar_t idname[KCDB_IDENT_MAXCCH_NAME]; + wchar_t realm[KCDB_IDENT_MAXCCH_NAME]; + wchar_t *r; + khm_size cbsize; + khm_handle ident; + int idx; + + if(d->nc->n_identities > 0) { + if(notif == CBN_SELCHANGE) { + idx = (int) SendDlgItemMessage(hwnd, IDC_NCK5_REALM, + CB_GETCURSEL, 0, 0); + SendDlgItemMessage(hwnd, IDC_NCK5_REALM, + CB_GETLBTEXT, idx, (LPARAM) realm); + } else { + GetDlgItemText(hwnd, IDC_NCK5_REALM, + realm, ARRAYLENGTH(realm)); + } + cbsize = sizeof(idname); + if(KHM_SUCCEEDED(kcdb_identity_get_name(d->nc->identities[0], + idname, &cbsize))) { + r = wcschr(idname, L'@'); + if(r && !wcscmp(realm, r+1)) + return 0; /* nothing to do */ + + if(!r) { + r = idname + wcslen(idname); + *r++ = L'@'; + *r++ = 0; + } + + /* if we get here, we have a new user */ + StringCchCopy(r+1, + ARRAYLENGTH(idname) - ((r+1) - idname), + realm); + if(KHM_SUCCEEDED(kcdb_identity_create(idname, + KCDB_IDENT_FLAG_CREATE, + &ident))) { + khui_cw_set_primary_id(d->nc, ident); + kcdb_identity_release(ident); + } + return 0; + } + } + + /* if we get here, we have a new realm, but there is no + identity */ + PostMessage(d->nc->hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_UPDATE_CREDTEXT), 0); + } + + return 0; +} + + +/* Dialog procedure for the Krb5 credentials type panel. + + NOTE: Runs in the context of the UI thread +*/ +INT_PTR CALLBACK +k5_nc_dlg_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + switch(uMsg) { + case WM_INITDIALOG: + return k5_handle_wm_initdialog(hwnd, wParam, lParam); + + case WM_COMMAND: + return k5_handle_wm_command(hwnd, wParam, lParam); + + case KHUI_WM_NC_NOTIFY: + return k5_handle_wmnc_notify(hwnd, wParam, lParam); + + case WM_DESTROY: + return k5_handle_wm_destroy(hwnd, wParam, lParam); + } + return FALSE; +} + +/* forward dcl */ +krb5_error_code KRB5_CALLCONV +k5_kinit_prompter(krb5_context context, + void *data, + const char *name, + const char *banner, + int num_prompts, + krb5_prompt prompts[]); + + + +fiber_job g_fjob; /* global fiber job object */ + +static BOOL +k5_cached_kinit_prompter(void); + +static BOOL +k5_cp_check_continue(void); + +/* + Runs in the context of the krb5 plugin's slave fiber +*/ +VOID CALLBACK +k5_kinit_fiber_proc(PVOID lpParameter) +{ + while(TRUE) + { + if(g_fjob.command == FIBER_CMD_KINIT) { + g_fjob.state = FIBER_STATE_KINIT; + + g_fjob.prompt_set = 0; + + if (k5_cached_kinit_prompter()) { + SwitchToFiber(k5_main_fiber); + + if (g_fjob.command != FIBER_CMD_CONTINUE) + goto _switch_to_main; + + if (!k5_cp_check_continue()) { + goto _switch_to_main; + } + } + + g_fjob.code = khm_krb5_kinit( + 0, + g_fjob.principal, + g_fjob.password, + g_fjob.ccache, + g_fjob.lifetime, + g_fjob.forwardable, + g_fjob.proxiable, + (g_fjob.renewable ? g_fjob.renew_life : 0), + g_fjob.addressless, + g_fjob.publicIP, + k5_kinit_prompter, + &g_fjob); + } + + _switch_to_main: + g_fjob.state = FIBER_STATE_NONE; + + SwitchToFiber(k5_main_fiber); + } +} + +/* return TRUE if we should go ahead with creds acquisition */ +static BOOL +k5_cp_check_continue(void) { + khm_size i; + khm_size n_p; + khui_new_creds_prompt * p; + size_t cch; + +#ifdef DEBUG + assert(g_fjob.nc); +#endif + + if (KHM_FAILED(khui_cw_get_prompt_count(g_fjob.nc, &n_p))) { +#ifdef DEBUG + assert(FALSE); +#endif + return TRUE; + } + + khui_cw_sync_prompt_values(g_fjob.nc); + + g_fjob.null_password = FALSE; + + /* we are just checking whether there was a password field that + was left empty, in which case we can't continue with the + credentials acquisition. */ + for (i=0; i < n_p; i++) { + if(KHM_FAILED(khui_cw_get_prompt(g_fjob.nc, + (int) i, + &p))) + continue; + if(p->type == KHUI_NCPROMPT_TYPE_PASSWORD) { + if (p->value == NULL || + FAILED(StringCchLength(p->value, KHUI_MAXCCH_PROMPT, + &cch)) || + cch == 0) { + g_fjob.null_password = TRUE; + return FALSE; + } else + break; + } + } + + return TRUE; +} + +/* returns true if we find cached prompts */ +static BOOL +k5_cached_kinit_prompter(void) { + BOOL rv = FALSE; + khm_handle ident; + khm_handle csp_idconfig = NULL; + khm_handle csp_k5config = NULL; + khm_handle csp_prcache = NULL; + khm_size cb; + khm_size n_cur_prompts; + khm_int32 n_prompts; + khm_int32 i; + +#ifdef DEBUG + assert(g_fjob.nc); +#endif + + ident = g_fjob.identity; + if (!ident) + return FALSE; + + /* don't need to hold ident, since it is already held in g_fjob + and it doesn't change until we return */ + + if (KHM_FAILED(kcdb_identity_get_config(ident, 0, &csp_idconfig)) || + + KHM_FAILED(khc_open_space(csp_idconfig, CSNAME_KRB5CRED, + 0, &csp_k5config)) || + + KHM_FAILED(khc_open_space(csp_k5config, CSNAME_PROMPTCACHE, + 0, &csp_prcache)) || + + KHM_FAILED(khc_read_int32(csp_prcache, L"PromptCount", + &n_prompts)) || + n_prompts == 0) + + goto _cleanup; + + /* check if there are any prompts currently showing. If there are + we check if they are the same as the ones we are going to show. + In which case we just reuse the exisitng prompts */ + if (KHM_FAILED(khui_cw_get_prompt_count(g_fjob.nc, + &n_cur_prompts)) || + n_prompts != (khm_int32) n_cur_prompts) + goto _show_new_prompts; + + for(i = 0; i < n_prompts; i++) { + wchar_t wsname[8]; + wchar_t wprompt[KHUI_MAXCCH_PROMPT]; + khm_handle csp_p = NULL; + khm_int32 p_type; + khm_int32 p_flags; + khui_new_creds_prompt * p; + + if (KHM_FAILED(khui_cw_get_prompt(g_fjob.nc, i, &p))) + break; + + StringCbPrintf(wsname, sizeof(wsname), L"%d", i); + + if (KHM_FAILED(khc_open_space(csp_prcache, wsname, 0, &csp_p))) + break; + + cb = sizeof(wprompt); + if (KHM_FAILED(khc_read_string(csp_p, L"Prompt", + wprompt, &cb))) { + khc_close_space(csp_p); + break; + } + + if (KHM_FAILED(khc_read_int32(csp_p, L"Type", &p_type))) + p_type = 0; + + if (KHM_FAILED(khc_read_int32(csp_p, L"Flags", &p_flags))) + p_flags = 0; + + if ( /* if we received a prompt string, + then it should be the same as the + one that is displayed */ + (wprompt[0] && + (p->prompt == NULL || + wcscmp(wprompt, p->prompt))) || + + /* if we didn't receive one, then + there shouldn't be one displayed. + This case really shouldn't happen + in reality, but we check anyway. */ + (!wprompt[0] && + p->prompt != NULL) || + + /* the type should match */ + (p_type != p->type) || + + /* if this prompt should be hidden, + then it must also be so */ + (p_flags != p->flags) + ) { + + khc_close_space(csp_p); + break; + + } + + + khc_close_space(csp_p); + } + + if (i == n_prompts) { + rv = TRUE; + goto _cleanup; + } + + _show_new_prompts: + + khui_cw_clear_prompts(g_fjob.nc); + + { + wchar_t wbanner[KHUI_MAXCCH_BANNER]; + wchar_t wpname[KHUI_MAXCCH_PNAME]; + + cb = sizeof(wbanner); + if (KHM_FAILED(khc_read_string(csp_prcache, L"Banner", + wbanner, &cb))) + wbanner[0] = 0; + + cb = sizeof(wpname); + if (KHM_FAILED(khc_read_string(csp_prcache, L"Name", + wpname, &cb))) + wpname[0] = 0; + + khui_cw_begin_custom_prompts(g_fjob.nc, + n_prompts, + (wbanner[0]? wbanner: NULL), + (wpname[0]? wpname: NULL)); + } + + for(i = 0; i < n_prompts; i++) { + wchar_t wsname[8]; + wchar_t wprompt[KHUI_MAXCCH_PROMPT]; + khm_handle csp_p = NULL; + khm_int32 p_type; + khm_int32 p_flags; + + StringCbPrintf(wsname, sizeof(wsname), L"%d", i); + + if (KHM_FAILED(khc_open_space(csp_prcache, wsname, 0, &csp_p))) + break; + + cb = sizeof(wprompt); + if (KHM_FAILED(khc_read_string(csp_p, L"Prompt", + wprompt, &cb))) { + khc_close_space(csp_p); + break; + } + + if (KHM_FAILED(khc_read_int32(csp_p, L"Type", &p_type))) + p_type = 0; + + if (KHM_FAILED(khc_read_int32(csp_p, L"Flags", &p_flags))) + p_flags = 0; + + khui_cw_add_prompt(g_fjob.nc, p_type, wprompt, NULL, p_flags); + + khc_close_space(csp_p); + } + + if (i < n_prompts) { + khui_cw_clear_prompts(g_fjob.nc); + } else { + rv = TRUE; + } + + _cleanup: + + if (csp_prcache) + khc_close_space(csp_prcache); + + if (csp_k5config) + khc_close_space(csp_k5config); + + if (csp_idconfig) + khc_close_space(csp_idconfig); + + return rv; +} + +/* Runs in the context of the Krb5 plugin's slave fiber */ +krb5_error_code KRB5_CALLCONV +k5_kinit_prompter(krb5_context context, + void *data, + const char *name, + const char *banner, + int num_prompts, + krb5_prompt prompts[]) +{ + int i; + khui_new_creds * nc; + krb5_prompt_type * ptypes; + khm_size ncp; + krb5_error_code code = 0; + BOOL new_prompts = TRUE; + + khm_handle csp_prcache = NULL; + + nc = g_fjob.nc; + + if(pkrb5_get_prompt_types) + ptypes = pkrb5_get_prompt_types(context); + else + ptypes = NULL; + + /* check if we are already showing the right prompts */ + khui_cw_get_prompt_count(nc, &ncp); + + if (num_prompts != (int) ncp) + goto _show_new_prompts; + + for (i=0; i < num_prompts; i++) { + wchar_t wprompt[KHUI_MAXCCH_PROMPT]; + khui_new_creds_prompt * p; + + if(prompts[i].prompt) { + AnsiStrToUnicode(wprompt, sizeof(wprompt), + prompts[i].prompt); + } else { + wprompt[0] = 0; + } + + if (KHM_FAILED(khui_cw_get_prompt(nc, i, &p))) + break; + + if ( /* if we received a prompt string, + then it should be the same as the + one that is displayed */ + (wprompt[0] && + (p->prompt == NULL || + wcscmp(wprompt, p->prompt))) || + /* if we didn't receive one, then + there shouldn't be one displayed. + This case really shouldn't happen + in reality, but we check anyway. */ + (!wprompt[0] && + p->prompt != NULL) || + /* the type should match */ + (ptypes && + ptypes[i] != p->type) || + (!ptypes && + p->type != 0) || + /* if this prompt should be hidden, + then it must also be so */ + (prompts[i].hidden && + !(p->flags & KHUI_NCPROMPT_FLAG_HIDDEN)) || + (!prompts[i].hidden && + (p->flags & KHUI_NCPROMPT_FLAG_HIDDEN)) + ) + break; + } + + if (i < num_prompts) + goto _show_new_prompts; + + new_prompts = FALSE; + + /* ok. looks like we are already showing the same set of prompts + that we were supposed to show. Sync up the values and go + ahead. */ + khui_cw_sync_prompt_values(nc); + goto _process_prompts; + + _show_new_prompts: + /* special case. if there are no actual input controls involved, + then we have to show an alerter window and pass through */ + if (num_prompts == 0) { + wchar_t wbanner[KHUI_MAXCCH_BANNER]; + wchar_t wname[KHUI_MAXCCH_PNAME]; + wchar_t wident[KCDB_IDENT_MAXCCH_NAME]; + wchar_t wmsg[KHUI_MAXCCH_MESSAGE]; + wchar_t wfmt[KHUI_MAXCCH_BANNER]; + khm_size cb; + + if (!banner) { + code = 0; + g_fjob.null_password = FALSE; + goto _exit; + } else { + AnsiStrToUnicode(wbanner, sizeof(wbanner), banner); + } + + if (name) { + AnsiStrToUnicode(wname, sizeof(wname), name); + } else { + LoadString(hResModule, + IDS_KRB5_WARNING, + wname, + ARRAYLENGTH(wname)); + } + + cb = sizeof(wident); + if (KHM_FAILED(kcdb_identity_get_name(g_fjob.identity, wident, &cb))) + wident[0] = L'\0'; + + LoadString(hResModule, + IDS_KRB5_WARN_FMT, + wfmt, + ARRAYLENGTH(wfmt)); + + StringCbPrintf(wmsg, sizeof(wmsg), wfmt, wident, wbanner); + + khui_alert_show_simple(wname, wmsg, KHERR_WARNING); + + code = 0; + g_fjob.null_password = FALSE; + goto _exit; + } + + /* in addition to showing new prompts, we also cache the set of + prompts. */ + if(g_fjob.prompt_set == 0) { + khm_handle csp_idconfig = NULL; + khm_handle csp_idk5 = NULL; + + kcdb_identity_get_config(g_fjob.identity, + KHM_FLAG_CREATE, + &csp_idconfig); + + if (csp_idconfig != NULL) + khc_open_space(csp_idconfig, + CSNAME_KRB5CRED, + KHM_FLAG_CREATE, + &csp_idk5); + + if (csp_idk5 != NULL) + khc_open_space(csp_idk5, + CSNAME_PROMPTCACHE, + KHM_FLAG_CREATE, + &csp_prcache); + + khc_close_space(csp_idconfig); + khc_close_space(csp_idk5); + } + + { + wchar_t wbanner[KHUI_MAXCCH_BANNER]; + wchar_t wname[KHUI_MAXCCH_PNAME]; + + if(banner) + AnsiStrToUnicode(wbanner, sizeof(wbanner), banner); + if(name) + AnsiStrToUnicode(wname, sizeof(wname), name); + + khui_cw_clear_prompts(nc); + + khui_cw_begin_custom_prompts( + nc, + num_prompts, + (banner)?wbanner:NULL, + (name)?wname:NULL); + + if (banner && csp_prcache) + khc_write_string(csp_prcache, + L"Banner", + wbanner); + else if (csp_prcache) + khc_write_string(csp_prcache, + L"Banner", + L""); + + if (name && csp_prcache) + khc_write_string(csp_prcache, + L"Name", + wname); + else if (csp_prcache) + khc_write_string(csp_prcache, + L"Name", + L""); + + if (csp_prcache) + khc_write_int32(csp_prcache, + L"PromptCount", + (khm_int32) num_prompts); + } + + for(i=0; i < num_prompts; i++) { + wchar_t wprompt[KHUI_MAXCCH_PROMPT]; + + if(prompts[i].prompt) { + AnsiStrToUnicode(wprompt, sizeof(wprompt), + prompts[i].prompt); + } else { + wprompt[0] = 0; + } + + khui_cw_add_prompt( + nc, + (ptypes?ptypes[i]:0), + wprompt, + NULL, + (prompts[i].hidden?KHUI_NCPROMPT_FLAG_HIDDEN:0)); + + if (csp_prcache) { + khm_handle csp_p = NULL; + wchar_t wnum[8]; /* should be enough for 10 + million prompts */ + + wnum[0] = 0; + StringCbPrintf(wnum, sizeof(wnum), L"%d", i); + + khc_open_space(csp_prcache, wnum, + KHM_FLAG_CREATE, &csp_p); + + if (csp_p) { + khc_write_string(csp_p, L"Prompt", wprompt); + khc_write_int32(csp_p, L"Type", (ptypes?ptypes[i]:0)); + khc_write_int32(csp_p, L"Flags", + (prompts[i].hidden? + KHUI_NCPROMPT_FLAG_HIDDEN:0)); + + khc_close_space(csp_p); + } + } + } + + if (csp_prcache) { + khc_close_space(csp_prcache); + csp_prcache = NULL; + } + + _process_prompts: + /* switch back to main thread if we showed new prompts */ + if (new_prompts) + SwitchToFiber(k5_main_fiber); + + /* we get here after the user selects an action that either + cancles the credentials acquisition operation or triggers the + actual acquisition of credentials. */ + if(g_fjob.command != FIBER_CMD_CONTINUE && + g_fjob.command != FIBER_CMD_KINIT) { + code = -2; + goto _exit; + } + + g_fjob.null_password = FALSE; + + /* otherwise, we need to get the data back from the UI and + return 0 */ + for(i=0; idata, d->length, wbuf); + if(SUCCEEDED(StringCchLengthA(d->data, d->length, &cch))) + d->length = (unsigned int) cch; + else + d->length = 0; + } else { +#ifdef DEBUG + assert(FALSE); +#endif + d->length = 0; + } + + if (ptypes && + ptypes[i] == KRB5_PROMPT_TYPE_PASSWORD && + d->length == 0) + + g_fjob.null_password = TRUE; + } + + _exit: + + g_fjob.prompt_set++; + + /* entering a NULL password is equivalent to cancelling out */ + if (g_fjob.null_password) + return -2; + else + return code; +} + + +void +k5_read_dlg_params(khm_handle conf, + k5_dlg_data * d) +{ + khm_int32 i; + + khc_read_int32(conf, L"Renewable", &d->renewable); + khc_read_int32(conf, L"Forwardable", &d->forwardable); + khc_read_int32(conf, L"Proxiable", &d->proxiable); + khc_read_int32(conf, L"Addressless", &d->addressless); + + khc_read_int32(conf, L"DefaultLifetime", &i); + d->tc_lifetime.current = i; + khc_read_int32(conf, L"MaxLifetime", &i); + d->tc_lifetime.max = i; + khc_read_int32(conf, L"MinLifetime", &i); + d->tc_lifetime.min = i; + + khc_read_int32(conf, L"DefaultRenewLifetime", &i); + d->tc_renew.current = i; + khc_read_int32(conf, L"MaxRenewLifetime", &i); + d->tc_renew.max = i; + khc_read_int32(conf, L"MinRenewLifetime", &i); + d->tc_renew.min = i; + + /* however, if this has externally supplied defaults, we have to + use them too. */ + if (d->nc && d->nc->ctx.vparam && + d->nc->ctx.cb_vparam == sizeof(NETID_DLGINFO)) { + LPNETID_DLGINFO pdlginfo; + + pdlginfo = (LPNETID_DLGINFO) d->nc->ctx.vparam; + if (pdlginfo->size == NETID_DLGINFO_V1_SZ && + pdlginfo->in.use_defaults == 0) { + d->forwardable = pdlginfo->in.forwardable; + d->addressless = pdlginfo->in.noaddresses; + d->tc_lifetime.current = pdlginfo->in.lifetime; + d->tc_renew.current = pdlginfo->in.renew_till; + + if (pdlginfo->in.renew_till == 0) + d->renewable = FALSE; + else + d->renewable = TRUE; + + d->proxiable = pdlginfo->in.proxiable; + d->publicIP = pdlginfo->in.publicip; + } + } + + /* once we read the new data, in, it is no longer considered + dirty */ + d->dirty = FALSE; +} + +void +k5_write_dlg_params(khm_handle conf, + k5_dlg_data * d) +{ + khc_write_int32(conf, L"Renewable", d->renewable); + khc_write_int32(conf, L"Forwardable", d->forwardable); + khc_write_int32(conf, L"Proxiable", d->proxiable); + khc_write_int32(conf, L"Addressless", d->addressless); + + khc_write_int32(conf, L"DefaultLifetime", + (khm_int32) d->tc_lifetime.current); + khc_write_int32(conf, L"MaxLifetime", + (khm_int32) d->tc_lifetime.max); + khc_write_int32(conf, L"MinLifetime", + (khm_int32) d->tc_lifetime.min); + + khc_write_int32(conf, L"DefaultRenewLifetime", + (khm_int32) d->tc_renew.current); + khc_write_int32(conf, L"MaxRenewLifetime", + (khm_int32) d->tc_renew.max); + khc_write_int32(conf, L"MinRenewLifetime", + (khm_int32) d->tc_renew.min); + + /* as in k5_read_dlg_params, once we write the data in, the local + data is no longer dirty */ + d->dirty = FALSE; +} + +void +k5_prep_kinit_job(khui_new_creds * nc) +{ + khui_new_creds_by_type * nct; + k5_dlg_data * d; + wchar_t idname[KCDB_IDENT_MAXCCH_NAME]; + khm_size cbbuf; + size_t size; + khm_handle ident; + LPNETID_DLGINFO pdlginfo; + + khui_cw_find_type(nc, credtype_id_krb5, &nct); + if (!nct) + return; + + d = (k5_dlg_data *)(LONG_PTR) + GetWindowLongPtr(nct->hwnd_panel, DWLP_USER); + + khui_cw_lock_nc(nc); + ident = nc->identities[0]; + kcdb_identity_hold(ident); + khui_cw_unlock_nc(nc); + + cbbuf = sizeof(idname); + kcdb_identity_get_name(ident, idname, &cbbuf); + StringCchLength(idname, ARRAYLENGTH(idname), &size); + size++; + + ZeroMemory(&g_fjob, sizeof(g_fjob)); + g_fjob.command = FIBER_CMD_KINIT; + g_fjob.nc = nc; + g_fjob.nct = nct; + g_fjob.dialog = nct->hwnd_panel; + g_fjob.principal = malloc(size); + UnicodeStrToAnsi(g_fjob.principal, size, idname); + g_fjob.password = NULL; + g_fjob.lifetime = (krb5_deltat) d->tc_lifetime.current; + g_fjob.forwardable = d->forwardable; + g_fjob.proxiable = d->proxiable; + g_fjob.renewable = d->renewable; + g_fjob.renew_life = (krb5_deltat) d->tc_renew.current; + g_fjob.addressless = d->addressless; + g_fjob.publicIP = 0; + g_fjob.code = 0; + g_fjob.identity = ident; + g_fjob.prompt_set = 0; + + /* if we have external parameters, we should use them as well */ + if (nc->ctx.cb_vparam == sizeof(NETID_DLGINFO) && + (pdlginfo = nc->ctx.vparam) && + pdlginfo->size == NETID_DLGINFO_V1_SZ) { + wchar_t * t; + + if (pdlginfo->in.ccache[0] && + SUCCEEDED(StringCchLength(pdlginfo->in.ccache, + NETID_CCACHE_NAME_SZ, + &size))) { + g_fjob.ccache = malloc(sizeof(char) * (size + 1)); +#ifdef DEBUG + assert(g_fjob.ccache); +#endif + UnicodeStrToAnsi(g_fjob.ccache, size + 1, + pdlginfo->in.ccache); + + /* this is the same as the output cache */ + + StringCbCopy(pdlginfo->out.ccache, sizeof(pdlginfo->out.ccache), + pdlginfo->in.ccache); + } else { + g_fjob.ccache = NULL; + + StringCbCopy(pdlginfo->out.ccache, sizeof(pdlginfo->out.ccache), + idname); + + khm_krb5_canon_cc_name(pdlginfo->out.ccache, + sizeof(pdlginfo->out.ccache)); + } + + t = khm_get_realm_from_princ(idname); + + if (t) { + StringCbCopy(pdlginfo->out.realm, + sizeof(pdlginfo->out.realm), + t); + + if ((t - idname) > 1) { + StringCchCopyN(pdlginfo->out.username, + ARRAYLENGTH(pdlginfo->out.username), + idname, + (t - idname) - 1); + } else { + StringCbCopy(pdlginfo->out.username, + sizeof(pdlginfo->out.username), + L""); + } + } else { + StringCbCopy(pdlginfo->out.username, + sizeof(pdlginfo->out.username), + idname); + StringCbCopy(pdlginfo->out.realm, + sizeof(pdlginfo->out.realm), + L""); + } + } + + /* leave identity held, since we added a reference above */ +} + +void +k5_free_kinit_job(void) +{ + if (g_fjob.principal) + free(g_fjob.principal); + + if (g_fjob.password) + free(g_fjob.password); + + if (g_fjob.identity) + kcdb_identity_release(g_fjob.identity); + + if (g_fjob.ccache) + free(g_fjob.ccache); + + ZeroMemory(&g_fjob, sizeof(g_fjob)); +} + +static khm_int32 KHMAPI +k5_find_tgt_filter(khm_handle cred, + khm_int32 flags, + void * rock) { + khm_handle ident = (khm_handle) rock; + khm_handle cident = NULL; + khm_int32 f; + khm_int32 rv; + + if (KHM_SUCCEEDED(kcdb_cred_get_identity(cred, + &cident)) && + cident == ident && + KHM_SUCCEEDED(kcdb_cred_get_flags(cred, &f)) && + (f & KCDB_CRED_FLAG_INITIAL)) + rv = 1; + else + rv = 0; + + if (cident) + kcdb_identity_release(cident); + + return rv; +} + +/* Handler for CRED type messages + + Runs in the context of the Krb5 plugin +*/ +khm_int32 KHMAPI +k5_msg_cred_dialog(khm_int32 msg_type, + khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + + switch(msg_subtype) { + + case KMSG_CRED_PASSWORD: + case KMSG_CRED_NEW_CREDS: + { + khui_new_creds * nc; + khui_new_creds_by_type * nct; + wchar_t wbuf[256]; + size_t cbsize; + + nc = (khui_new_creds *) vparam; + + nct = malloc(sizeof(*nct)); + ZeroMemory(nct, sizeof(*nct)); + + nct->type = credtype_id_krb5; + nct->ordinal = 1; + + LoadString(hResModule, IDS_KRB5_SHORT_DESC, + wbuf, ARRAYLENGTH(wbuf)); + StringCbLength(wbuf, sizeof(wbuf), &cbsize); + cbsize += sizeof(wchar_t); + + nct->name = malloc(cbsize); + StringCbCopy(nct->name, cbsize, wbuf); + + nct->h_module = hResModule; + nct->dlg_proc = k5_nc_dlg_proc; + if (nc->subtype == KMSG_CRED_PASSWORD) + nct->dlg_template = MAKEINTRESOURCE(IDD_NC_KRB5_PASSWORD); + else + nct->dlg_template = MAKEINTRESOURCE(IDD_NC_KRB5); + + khui_cw_add_type(nc, nct); + } + break; + + case KMSG_CRED_RENEW_CREDS: + { + khui_new_creds * nc; + khui_new_creds_by_type * nct; + + nc = (khui_new_creds *) vparam; + + nct = malloc(sizeof(*nct)); + ZeroMemory(nct, sizeof(*nct)); + + nct->type = credtype_id_krb5; + + khui_cw_add_type(nc, nct); + } + break; + + case KMSG_CRED_DIALOG_PRESTART: + { + khui_new_creds * nc; + khui_new_creds_by_type * nct; + k5_dlg_data * d; + HWND hwnd; + wchar_t * realms; + wchar_t * t; + wchar_t * defrealm; + + nc = (khui_new_creds *) vparam; + + khui_cw_find_type(nc, credtype_id_krb5, &nct); + + if(!nct) + break; + + hwnd = nct->hwnd_panel; + d = (k5_dlg_data *)(LONG_PTR) + GetWindowLongPtr(nct->hwnd_panel, DWLP_USER); + + if (!is_k5_identpro) { + + /* enumerate all realms and place in realms combo box */ + SendDlgItemMessage(hwnd, IDC_NCK5_REALM, + CB_RESETCONTENT, + 0, 0); + + realms = khm_krb5_get_realm_list(); + if(realms) { + t = realms; + while(t && *t) { + SendDlgItemMessage(hwnd, IDC_NCK5_REALM, + CB_ADDSTRING, + 0, (LPARAM) t); + t = multi_string_next(t); + } + free(realms); + } + + /* and set the default realm */ + defrealm = khm_krb5_get_default_realm(); + if(defrealm) { + SendDlgItemMessage(hwnd, IDC_NCK5_REALM, + CB_SELECTSTRING, + (WPARAM) -1, + (LPARAM) defrealm); + + SendDlgItemMessage(hwnd, IDC_NCK5_REALM, + WM_SETTEXT, + 0, (LPARAM) defrealm); + free(defrealm); + } + } else { /* if krb5 is the identity provider */ + HWND hw_realms; + + /* in this case, the realm selection is done by the + identity provider prompts. */ + + hw_realms = GetDlgItem(hwnd, IDC_NCK5_REALM); +#ifdef DEBUG + assert(hw_realms); +#endif + EnableWindow(hw_realms, FALSE); + } + + if (nc->subtype == KMSG_CRED_NEW_CREDS) { + k5_read_dlg_params(csp_params, d); + } + + PostMessage(hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0,WMNC_DIALOG_SETUP), 0); + } + break; + + case KMSG_CRED_DIALOG_NEW_IDENTITY: + { + khui_new_creds * nc; + khui_new_creds_by_type * nct; + k5_dlg_data * d; + + nc = (khui_new_creds *) vparam; + + khui_cw_find_type(nc, credtype_id_krb5, &nct); + if (!nct) + break; + + d = (k5_dlg_data *)(LONG_PTR) + GetWindowLongPtr(nct->hwnd_panel, DWLP_USER); + + /* we only load the identity specific defaults if the user + hasn't changed the options */ + khui_cw_lock_nc(nc); + + if(!d->dirty && nc->n_identities > 0 && + nc->subtype == KMSG_CRED_NEW_CREDS) { + + khm_handle h_id = NULL; + khm_handle h_idk5 = NULL; + + do { + if(KHM_FAILED + (kcdb_identity_get_config(nc->identities[0], + 0, + &h_id))) + break; + + if(KHM_FAILED + (khc_open_space(h_id, CSNAME_KRB5CRED, + 0, &h_idk5))) + break; + + if(KHM_FAILED(khc_shadow_space(h_idk5, csp_params))) + break; + + k5_read_dlg_params(h_idk5, d); + + PostMessage(nct->hwnd_panel, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0,WMNC_DIALOG_SETUP), 0); + } while(FALSE); + + if(h_id) + khc_close_space(h_id); + if(h_idk5) + khc_close_space(h_idk5); + } + + khui_cw_unlock_nc(nc); + } + + /* fallthrough */ + case KMSG_CRED_DIALOG_NEW_OPTIONS: + { + khui_new_creds * nc; + khui_new_creds_by_type * nct; + k5_dlg_data * d; + + nc = (khui_new_creds *) vparam; + + khui_cw_find_type(nc, credtype_id_krb5, &nct); + if (!nct) + break; + + d = (k5_dlg_data *)(LONG_PTR) + GetWindowLongPtr(nct->hwnd_panel, DWLP_USER); + + if (nc->subtype == KMSG_CRED_PASSWORD) { + khm_size n_prompts = 0; + + khui_cw_get_prompt_count(nc, &n_prompts); + + if (nc->n_identities == 0) { + if (n_prompts) + khui_cw_clear_prompts(nc); + } else if (n_prompts != 3) { + wchar_t wbuf[KHUI_MAXCCH_BANNER]; + + khui_cw_clear_prompts(nc); + + LoadString(hResModule, IDS_NC_PWD_BANNER, + wbuf, ARRAYLENGTH(wbuf)); + khui_cw_begin_custom_prompts(nc, 3, NULL, wbuf); + + LoadString(hResModule, IDS_NC_PWD_PWD, + wbuf, ARRAYLENGTH(wbuf)); + khui_cw_add_prompt(nc, KHUI_NCPROMPT_TYPE_PASSWORD, + wbuf, NULL, KHUI_NCPROMPT_FLAG_HIDDEN); + + LoadString(hResModule, IDS_NC_PWD_NPWD, + wbuf, ARRAYLENGTH(wbuf)); + khui_cw_add_prompt(nc, KHUI_NCPROMPT_TYPE_NEW_PASSWORD, + wbuf, NULL, KHUI_NCPROMPT_FLAG_HIDDEN); + + LoadString(hResModule, IDS_NC_PWD_NPWD_AGAIN, + wbuf, ARRAYLENGTH(wbuf)); + khui_cw_add_prompt(nc, KHUI_NCPROMPT_TYPE_NEW_PASSWORD_AGAIN, + wbuf, NULL, KHUI_NCPROMPT_FLAG_HIDDEN); + } + + return KHM_ERROR_SUCCESS; + } + /* else; nc->subtype == KMSG_CRED_NEW_CREDS */ + + assert(nc->subtype == KMSG_CRED_NEW_CREDS); + + /* if the fiber is already in a kinit, cancel it */ + if(g_fjob.state == FIBER_STATE_KINIT) { + g_fjob.command = FIBER_CMD_CANCEL; + SwitchToFiber(k5_kinit_fiber); + /* we get here when the cancel operation completes */ + k5_free_kinit_job(); + } + + khui_cw_lock_nc(nc); + + if(nc->n_identities > 0) { + khm_handle ident = nc->identities[0]; + + kcdb_identity_hold(ident); + + k5_prep_kinit_job(nc); + khui_cw_unlock_nc(nc); + + SwitchToFiber(k5_kinit_fiber); + /* we get here when the fiber switches back */ + if(g_fjob.state == FIBER_STATE_NONE) { + wchar_t msg[KHUI_MAXCCH_BANNER]; + khm_size cb; + + /* we can't possibly have succeeded without a + password */ + if(g_fjob.code) { + if (is_k5_identpro) + kcdb_identity_set_flags(ident, + KCDB_IDENT_FLAG_INVALID); + + khui_cw_clear_prompts(nc); + } + + if (d->cred_message) { + free(d->cred_message); + d->cred_message = NULL; + } + + msg[0] = L'\0'; + + switch(g_fjob.code) { + case KRB5KDC_ERR_NAME_EXP: + /* principal expired */ + LoadString(hResModule, IDS_K5ERR_NAME_EXPIRED, + msg, ARRAYLENGTH(msg)); + break; + + case KRB5KDC_ERR_KEY_EXP: + /* password needs changing */ + LoadString(hResModule, IDS_K5ERR_KEY_EXPIRED, + msg, ARRAYLENGTH(msg)); + break; + + default: + { + DWORD dw_dummy; + kherr_suggestion sug_dummy; + wchar_t fmt[KHUI_MAXCCH_BANNER]; + wchar_t desc[KHUI_MAXCCH_BANNER]; + + LoadString(hResModule, IDS_K5ERR_FMT, + fmt, ARRAYLENGTH(fmt)); + + khm_err_describe(g_fjob.code, + desc, + sizeof(desc), + &dw_dummy, + &sug_dummy); + + StringCbPrintf(msg, sizeof(msg), fmt, desc); + } + } + + if (msg[0]) { + StringCbLength(msg, sizeof(msg), &cb); + cb += sizeof(wchar_t); + + d->cred_message = malloc(cb); + StringCbCopy(d->cred_message, cb, msg); + } + + k5_free_kinit_job(); + + } else if(g_fjob.state == FIBER_STATE_KINIT) { + /* this is what we want. Leave the fiber there. */ + + if(is_k5_identpro) + kcdb_identity_set_flags(ident, + KCDB_IDENT_FLAG_VALID); + } else { + /* huh?? */ +#ifdef DEBUG + assert(FALSE); +#endif + } + + /* since the attributes of the identity have changed, + we should update the cred text as well */ + kcdb_identity_release(ident); + khui_cw_lock_nc(nc); + PostMessage(nc->hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_UPDATE_CREDTEXT), 0); + } else { + khui_cw_unlock_nc(nc); + khui_cw_clear_prompts(nc); + khui_cw_lock_nc(nc); + } + + khui_cw_unlock_nc(nc); + } + break; + + case KMSG_CRED_PROCESS: + { + khui_new_creds * nc; + khui_new_creds_by_type * nct; + k5_dlg_data * d; + + khm_int32 r; + + nc = (khui_new_creds *) vparam; + + khui_cw_find_type(nc, credtype_id_krb5, &nct); + + if(!nct) + break; + + /* reset the null_password flag, just in case */ + g_fjob.null_password = FALSE; + + if (nc->subtype == KMSG_CRED_NEW_CREDS) { + d = (k5_dlg_data *) nct->aux; + + _begin_task(0); + _report_mr0(KHERR_NONE, MSG_CTX_INITAL_CREDS); + _describe(); + + if (g_fjob.state == FIBER_STATE_KINIT) { + if(nc->result == KHUI_NC_RESULT_CANCEL) { + g_fjob.command = FIBER_CMD_CANCEL; + SwitchToFiber(k5_kinit_fiber); + + /* if we cancelled out, then we shouldn't care + about the return code. */ +#ifdef DEBUG + assert(g_fjob.state == FIBER_STATE_NONE); +#endif + g_fjob.code = 0; + } else if (nc->result == KHUI_NC_RESULT_GET_CREDS) { + khui_cw_sync_prompt_values(nc); + g_fjob.command = FIBER_CMD_CONTINUE; + SwitchToFiber(k5_kinit_fiber); + + /* We get back here once the fiber finishes + processing */ + } +#ifdef DEBUG + else { + assert(FALSE); + } +#endif + } else { + /* we weren't in a KINIT state */ + if (nc->result == KHUI_NC_RESULT_CANCEL) { + /* nothing to report */ + g_fjob.code = 0; + } else if (nc->result == KHUI_NC_RESULT_GET_CREDS) { + /* g_fjob.code should have the result of the + last kinit attempt. We should leave it + as-is */ + } +#ifdef DEBUG + else { + /* unknown result */ + assert(FALSE); + } +#endif + } + + /* special case: if there was no password entered, and + if there is a valid TGT we allow the credential + acquisition to go through */ + if (g_fjob.state == FIBER_STATE_NONE && + g_fjob.code && + g_fjob.null_password && + + (nc->n_identities == 0 || + nc->identities[0] == NULL || + KHM_SUCCEEDED(kcdb_credset_find_filtered + (NULL, + -1, + k5_find_tgt_filter, + nc->identities[0], + NULL, + NULL)))) + g_fjob.code = 0; + + + if(g_fjob.code != 0) { + wchar_t tbuf[1024]; + DWORD suggestion; + kherr_suggestion suggest_code; + + khm_err_describe(g_fjob.code, tbuf, sizeof(tbuf), + &suggestion, &suggest_code); + + _report_cs0(KHERR_ERROR, tbuf); + if (suggestion != 0) + _suggest_mr(suggestion, suggest_code); + + _resolve(); + + r = KHUI_NC_RESPONSE_FAILED; + + if (suggest_code == KHERR_SUGGEST_RETRY) { + r |= KHUI_NC_RESPONSE_NOEXIT | + KHUI_NC_RESPONSE_PENDING; + } + +#ifdef DEBUG + assert(g_fjob.state == FIBER_STATE_NONE); +#endif + + } else if (nc->result == KHUI_NC_RESULT_GET_CREDS && + g_fjob.state == FIBER_STATE_NONE) { + khm_handle sp = NULL; + khm_handle ep = NULL; + krb5_context ctx = NULL; + wchar_t * wbuf; + wchar_t * idname; + wchar_t * atsign; + khm_size cb; + khm_size cb_ms; + khm_int32 rv; + + r = KHUI_NC_RESPONSE_SUCCESS | + KHUI_NC_RESPONSE_EXIT; + + /* if we successfully obtained credentials, we + should save the current settings in the + identity config space */ + + assert(nc->n_identities > 0); + assert(nc->identities[0]); + + if(KHM_SUCCEEDED + (kcdb_identity_get_config(nc->identities[0], + KHM_FLAG_CREATE, + &sp)) && + KHM_SUCCEEDED + (khc_open_space(sp, CSNAME_KRB5CRED, + KHM_FLAG_CREATE, &ep))) { + k5_write_dlg_params(ep, d); + } + + if(ep != NULL) + khc_close_space(ep); + if(sp != NULL) + khc_close_space(sp); + + /* We should also quickly refresh the credentials + so that the identity flags and ccache + properties reflect the current state of + affairs. This has to be done here so that + other credentials providers which depend on + Krb5 can properly find the initial creds to + obtain their respective creds. */ + + khm_krb5_list_tickets(&ctx); + + /* also add the principal and the realm in to the + LRU lists */ + rv = kcdb_identity_get_name(nc->identities[0], + NULL, + &cb); + assert(rv == KHM_ERROR_TOO_LONG); + + idname = malloc(cb); + assert(idname); + + rv = kcdb_identity_get_name(nc->identities[0], + idname, + &cb); + assert(KHM_SUCCEEDED(rv)); + + rv = khc_read_multi_string(csp_params, + L"LRUPrincipals", + NULL, + &cb_ms); + if (rv != KHM_ERROR_TOO_LONG) + cb_ms = cb + sizeof(wchar_t); + else + cb_ms += cb + sizeof(wchar_t); + + wbuf = malloc(cb_ms); + assert(wbuf); + + cb = cb_ms; + + if (rv == KHM_ERROR_TOO_LONG) { + rv = khc_read_multi_string(csp_params, + L"LRUPrincipals", + wbuf, + &cb); + assert(KHM_SUCCEEDED(rv)); + + if (multi_string_find(wbuf, + idname, + KHM_CASE_SENSITIVE) + != NULL) + /* it's already there */ + goto _add_realm_to_LRU; + } else { + multi_string_init(wbuf, cb_ms); + } + + cb = cb_ms; + rv = multi_string_prepend(wbuf, &cb, idname); + assert(KHM_SUCCEEDED(rv)); + + rv = khc_write_multi_string(csp_params, + L"LRUPrincipals", + wbuf); + + _add_realm_to_LRU: + + atsign = wcschr(idname, L'@'); + assert(atsign != NULL); + + atsign++; + assert(*atsign != L'\0'); + + cb = cb_ms; + rv = khc_read_multi_string(csp_params, + L"LRURealms", + wbuf, + &cb); + + if (rv == KHM_ERROR_TOO_LONG) { + free(wbuf); + wbuf = malloc(cb); + assert(wbuf); + + cb_ms = cb; + + rv = khc_read_multi_string(csp_params, + L"LRURealms", + wbuf, + &cb); + + assert(KHM_SUCCEEDED(rv)); + } else if (rv == KHM_ERROR_SUCCESS) { + if (multi_string_find(wbuf, + atsign, + KHM_CASE_SENSITIVE) + != NULL) + goto _done_with_LRU; + } else { + multi_string_init(wbuf, cb_ms); + } + + cb = cb_ms; + rv = multi_string_prepend(wbuf, + &cb, + atsign); + + if (rv == KHM_ERROR_TOO_LONG) { + wbuf = realloc(wbuf, cb); + + rv = multi_string_prepend(wbuf, + &cb, + atsign); + + assert(KHM_SUCCEEDED(rv)); + } + + rv = khc_write_multi_string(csp_params, + L"LRURealms", + wbuf); + assert(KHM_SUCCEEDED(rv)); + + _done_with_LRU: + + if (ctx != NULL) + pkrb5_free_context(ctx); + + if (idname) + free(idname); + + if (wbuf) + free(wbuf); + } else if (g_fjob.state == FIBER_STATE_NONE) { + /* the user cancelled the operation */ + r = KHUI_NC_RESPONSE_EXIT | + KHUI_NC_RESPONSE_SUCCESS; + } + + if(g_fjob.state == FIBER_STATE_NONE) { + khui_cw_set_response(nc, credtype_id_krb5, r); + + if (r & KHUI_NC_RESPONSE_NOEXIT) { + /* if we are retrying the call, we should + restart the kinit fiber */ +#ifdef DEBUG + assert(r & KHUI_NC_RESPONSE_PENDING); +#endif + + k5_prep_kinit_job(nc); + SwitchToFiber(k5_kinit_fiber); + } else { + /* free up the fiber data fields. */ + k5_free_kinit_job(); + } + } else { + khui_cw_set_response(nc, credtype_id_krb5, + KHUI_NC_RESPONSE_NOEXIT | + KHUI_NC_RESPONSE_PENDING | r); + } + + _end_task(); + } else if (nc->subtype == KMSG_CRED_RENEW_CREDS) { + + _begin_task(0); + _report_mr0(KHERR_NONE, MSG_CTX_RENEW_CREDS); + _describe(); + + if (nc->ctx.scope == KHUI_SCOPE_IDENT || + (nc->ctx.scope == KHUI_SCOPE_CREDTYPE && + nc->ctx.cred_type == credtype_id_krb5)) { + int code; + + if (nc->ctx.identity != 0) + code = khm_krb5_renew(nc->ctx.identity); + else + code = 1; /* it just has to be non-zero */ + + if (code == 0) { + khui_cw_set_response(nc, credtype_id_krb5, + KHUI_NC_RESPONSE_EXIT | + KHUI_NC_RESPONSE_SUCCESS); + } else if (nc->ctx.identity == 0) { + + _report_mr0(KHERR_ERROR, MSG_ERR_NO_IDENTITY); + + khui_cw_set_response(nc, credtype_id_krb5, + KHUI_NC_RESPONSE_EXIT | + KHUI_NC_RESPONSE_FAILED); + } else { + wchar_t tbuf[1024]; + DWORD suggestion; + kherr_suggestion sug_id; + + khm_err_describe(code, tbuf, sizeof(tbuf), + &suggestion, &sug_id); + + _report_cs0(KHERR_ERROR, tbuf); + if (suggestion) + _suggest_mr(suggestion, sug_id); + + _resolve(); + + khui_cw_set_response(nc, credtype_id_krb5, + ((sug_id == KHERR_SUGGEST_RETRY)?KHUI_NC_RESPONSE_NOEXIT:KHUI_NC_RESPONSE_EXIT) | + KHUI_NC_RESPONSE_FAILED); + } + } else { + khui_cw_set_response(nc, credtype_id_krb5, + KHUI_NC_RESPONSE_EXIT | + KHUI_NC_RESPONSE_SUCCESS); + } + + _end_task(); + } else if (nc->subtype == KMSG_CRED_PASSWORD && + nc->result == KHUI_NC_RESULT_GET_CREDS) { + + _begin_task(0); + _report_mr0(KHERR_NONE, MSG_CTX_PASSWD); + _describe(); + + khui_cw_lock_nc(nc); + + if (nc->n_identities == 0 || + nc->identities[0] == NULL) { + _report_mr0(KHERR_ERROR, MSG_PWD_NO_IDENTITY); + _suggest_mr(MSG_PWD_S_NO_IDENTITY, KHERR_SUGGEST_RETRY); + + khui_cw_set_response(nc, credtype_id_krb5, + KHUI_NC_RESPONSE_FAILED | + KHUI_NC_RESPONSE_NOEXIT); + } else { + wchar_t widname[KCDB_IDENT_MAXCCH_NAME]; + char idname[KCDB_IDENT_MAXCCH_NAME]; + wchar_t wpwd[KHUI_MAXCCH_PASSWORD]; + char pwd[KHUI_MAXCCH_PASSWORD]; + wchar_t wnpwd[KHUI_MAXCCH_PASSWORD]; + char npwd[KHUI_MAXCCH_PASSWORD]; + wchar_t wnpwd2[KHUI_MAXCCH_PASSWORD]; + wchar_t * wresult; + char * result; + khm_size n_prompts = 0; + khm_size cb; + khm_int32 rv = KHM_ERROR_SUCCESS; + long code = 0; + khm_handle ident; + + khui_cw_get_prompt_count(nc, &n_prompts); + assert(n_prompts == 3); + + ident = nc->identities[0]; + cb = sizeof(widname); + rv = kcdb_identity_get_name(ident, widname, &cb); + if (KHM_FAILED(rv)) { +#ifdef DEBUG + assert(FALSE); +#endif + _report_mr0(KHERR_ERROR, MSG_PWD_UNKNOWN); + goto _pwd_exit; + } + + cb = sizeof(wpwd); + rv = khui_cw_get_prompt_value(nc, 0, wpwd, &cb); + if (KHM_FAILED(rv)) { +#ifdef DEBUG + assert(FALSE); +#endif + _report_mr0(KHERR_ERROR, MSG_PWD_UNKNOWN); + goto _pwd_exit; + } + + cb = sizeof(wnpwd); + rv = khui_cw_get_prompt_value(nc, 1, wnpwd, &cb); + if (KHM_FAILED(rv)) { +#ifdef DEBUG + assert(FALSE); +#endif + _report_mr0(KHERR_ERROR, MSG_PWD_UNKNOWN); + goto _pwd_exit; + } + + cb = sizeof(wnpwd2); + rv = khui_cw_get_prompt_value(nc, 2, wnpwd2, &cb); + if (KHM_FAILED(rv)) { +#ifdef DEBUG + assert(FALSE); +#endif + _report_mr0(KHERR_ERROR, MSG_PWD_UNKNOWN); + goto _pwd_exit; + } + + if (wcscmp(wnpwd, wnpwd2)) { + rv = KHM_ERROR_INVALID_PARM; + _report_mr0(KHERR_ERROR, MSG_PWD_NOT_SAME); + _suggest_mr(MSG_PWD_S_NOT_SAME, KHERR_SUGGEST_INTERACT); + goto _pwd_exit; + } + + if (!wcscmp(wpwd, wnpwd)) { + rv = KHM_ERROR_INVALID_PARM; + _report_mr0(KHERR_ERROR, MSG_PWD_SAME); + _suggest_mr(MSG_PWD_S_SAME, KHERR_SUGGEST_INTERACT); + goto _pwd_exit; + } + + UnicodeStrToAnsi(idname, sizeof(idname), widname); + UnicodeStrToAnsi(pwd, sizeof(pwd), wpwd); + UnicodeStrToAnsi(npwd, sizeof(npwd), wnpwd); + + result = NULL; + + code = khm_krb5_changepwd(idname, + pwd, + npwd, + &result); + + if (code) + rv = KHM_ERROR_UNKNOWN; + + /* result is only set when code != 0 */ + if (code && result) { + size_t len; + + StringCchLengthA(result, KHERR_MAXCCH_STRING, + &len); + wresult = malloc((len + 1) * sizeof(wchar_t)); +#ifdef DEBUG + assert(wresult); +#endif + AnsiStrToUnicode(wresult, (len + 1) * sizeof(wchar_t), + result); + + _report_cs1(KHERR_ERROR, L"%1!s!", _cstr(wresult)); + _resolve(); + + free(result); + free(wresult); + + /* leave wresult. It will get freed when the + reported event is freed. */ + + /* we don't need to report anything more */ + code = 0; + } + + _pwd_exit: + if (KHM_FAILED(rv)) { + if (code) { + wchar_t tbuf[1024]; + DWORD suggestion; + kherr_suggestion sug_id; + + khm_err_describe(code, tbuf, sizeof(tbuf), + &suggestion, &sug_id); + _report_cs0(KHERR_ERROR, tbuf); + + if (suggestion) + _suggest_mr(suggestion, sug_id); + + _resolve(); + } + + khui_cw_set_response(nc, credtype_id_krb5, + KHUI_NC_RESPONSE_NOEXIT| + KHUI_NC_RESPONSE_FAILED); + } else { + khui_cw_set_response(nc, credtype_id_krb5, + KHUI_NC_RESPONSE_SUCCESS | + KHUI_NC_RESPONSE_EXIT); + } + } + + khui_cw_unlock_nc(nc); + + _end_task(); + } /* KMSG_CRED_PASSWORD */ + } + break; + + case KMSG_CRED_END: + { + khui_new_creds * nc; + khui_new_creds_by_type * nct; + + nc = (khui_new_creds *) vparam; + khui_cw_find_type(nc, credtype_id_krb5, &nct); + + if(!nct) + break; + + khui_cw_del_type(nc, credtype_id_krb5); + + if(nct->name) + free(nct->name); + + free(nct); + } + break; + + case KMSG_CRED_IMPORT: + { + khm_krb5_ms2mit(TRUE); + } + break; + } + + return rv; +} diff --git a/src/windows/identity/plugins/krb5/krb5plugin.c b/src/windows/identity/plugins/krb5/krb5plugin.c new file mode 100644 index 000000000..4b53ed3e8 --- /dev/null +++ b/src/windows/identity/plugins/krb5/krb5plugin.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include + +khm_int32 credtype_id_krb5 = KCDB_CREDTYPE_INVALID; +khm_boolean krb5_initialized = FALSE; +khm_handle krb5_credset = NULL; + +khm_handle k5_sub = NULL; + +LPVOID k5_main_fiber = NULL; +LPVOID k5_kinit_fiber = NULL; + +VOID CALLBACK k5_kinit_fiber_proc(PVOID lpParameter); + +krb5_context k5_identpro_ctx = NULL; + +/* The system message handler. + + Runs in the context of the plugin thread */ +khm_int32 KHMAPI k5_msg_system(khm_int32 msg_type, khm_int32 msg_subtype, khm_ui_4 uparam, void * vparam) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + + switch(msg_subtype) { + case KMSG_SYSTEM_INIT: + { + kcdb_credtype ct; + wchar_t buf[KCDB_MAXCCH_SHORT_DESC]; + size_t cbsize; + + /* perform critical registrations and initialization + stuff */ + ZeroMemory(&ct, sizeof(ct)); + ct.id = KCDB_CREDTYPE_AUTO; + ct.name = KRB5_CREDTYPE_NAME; + + if(LoadString(hResModule, IDS_KRB5_SHORT_DESC, buf, ARRAYLENGTH(buf))) + { + StringCbLength(buf, KCDB_MAXCB_SHORT_DESC, &cbsize); + cbsize += sizeof(wchar_t); + ct.short_desc = malloc(cbsize); + StringCbCopy(ct.short_desc, cbsize, buf); + } + + /* even though ideally we should be setting limits + based KCDB_MAXCB_LONG_DESC, our long description + actually fits nicely in KCDB_MAXCB_SHORT_DESC */ + if(LoadString(hResModule, IDS_KRB5_LONG_DESC, buf, ARRAYLENGTH(buf))) + { + StringCbLength(buf, KCDB_MAXCB_SHORT_DESC, &cbsize); + cbsize += sizeof(wchar_t); + ct.long_desc = malloc(cbsize); + StringCbCopy(ct.long_desc, cbsize, buf); + } + + ct.icon = NULL; /* TODO: set a proper icon */ + + kmq_create_subscription(k5_msg_callback, &ct.sub); + + rv = kcdb_credtype_register(&ct, &credtype_id_krb5); + + if(KHM_SUCCEEDED(rv)) + rv = kcdb_credset_create(&krb5_credset); + + if(ct.short_desc) + free(ct.short_desc); + + if(ct.long_desc) + free(ct.long_desc); + + if (is_k5_identpro) + kcdb_identity_set_type(credtype_id_krb5); + + if(KHM_SUCCEEDED(rv)) { + krb5_context ctx = NULL; + + krb5_initialized = TRUE; + + khm_krb5_list_tickets(&ctx); + + if(ctx != NULL) + pkrb5_free_context(ctx); + + /* now convert this thread to a fiber and create a + separate fiber to do kinit stuff */ + k5_main_fiber = ConvertThreadToFiber(NULL); + k5_kinit_fiber = CreateFiber(0,k5_kinit_fiber_proc,NULL); + + ZeroMemory(&g_fjob, sizeof(g_fjob)); + + kmq_create_subscription(k5_msg_callback, &k5_sub); + + pkrb5_init_context(&k5_identpro_ctx); + + k5_register_config_panels(); + } + } + break; + + case KMSG_SYSTEM_EXIT: + + k5_unregister_config_panels(); + + if(credtype_id_krb5 >= 0) + { + /* basically just unregister the credential type */ + kcdb_credtype_unregister(credtype_id_krb5); + + /* kcdb knows how to deal with bad handles */ + kcdb_credset_delete(krb5_credset); + krb5_credset = NULL; + } + + if(k5_main_fiber != NULL) { + ConvertFiberToThread(); + k5_main_fiber = NULL; + } + + if(k5_sub != NULL) { + kmq_delete_subscription(k5_sub); + k5_sub = NULL; + } + + if (k5_identpro_ctx) { + pkrb5_free_context(k5_identpro_ctx); + k5_identpro_ctx = NULL; + } + + break; + } + + return rv; +} + + +/* Handler for CRED type messages + + Runs in the context of the Krb5 plugin +*/ +khm_int32 KHMAPI k5_msg_cred(khm_int32 msg_type, khm_int32 msg_subtype, khm_ui_4 uparam, void * vparam) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + + switch(msg_subtype) { + case KMSG_CRED_REFRESH: + { + krb5_context ctx = NULL; + + khm_krb5_list_tickets(&ctx); + + if(ctx != NULL) + pkrb5_free_context(ctx); + } + break; + + case KMSG_CRED_DESTROY_CREDS: + { + khui_action_context * ctx; + + ctx = (khui_action_context *) vparam; + + if (ctx->credset) + khm_krb5_destroy_by_credset(ctx->credset); + } + break; + + case KMSG_CRED_PP_BEGIN: + k5_pp_begin((khui_property_sheet *) vparam); + break; + + case KMSG_CRED_PP_END: + k5_pp_end((khui_property_sheet *) vparam); + break; + + default: + if(IS_CRED_ACQ_MSG(msg_subtype)) + return k5_msg_cred_dialog(msg_type, msg_subtype, + uparam, vparam); + } + + return rv; +} + +/* The main message handler. We don't do much here, except delegate + to other message handlers + + Runs in the context of the Krb5 plugin +*/ +khm_int32 KHMAPI k5_msg_callback(khm_int32 msg_type, khm_int32 msg_subtype, khm_ui_4 uparam, void * vparam) +{ + switch(msg_type) { + case KMSG_SYSTEM: + return k5_msg_system(msg_type, msg_subtype, uparam, vparam); + case KMSG_CRED: + return k5_msg_cred(msg_type, msg_subtype, uparam, vparam); + case KMSG_IDENT: + return k5_msg_ident(msg_type, msg_subtype, uparam, vparam); + } + return KHM_ERROR_SUCCESS; +} diff --git a/src/windows/identity/plugins/krb5/krb5props.c b/src/windows/identity/plugins/krb5/krb5props.c new file mode 100644 index 000000000..9134de292 --- /dev/null +++ b/src/windows/identity/plugins/krb5/krb5props.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include + +/* Property page + + Runs in the context of the UI thread. + */ +INT_PTR CALLBACK krb5_pp_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ) +{ + switch(uMsg) { + case WM_INITDIALOG: + { + khui_property_sheet * s; + PROPSHEETPAGE * p; + wchar_t buf[512]; + khm_size cbsize; + + p = (PROPSHEETPAGE *) lParam; + s = (khui_property_sheet *) p->lParam; + +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) s); +#pragma warning(pop) + + if(s->cred) { + cbsize = sizeof(buf); + kcdb_cred_get_name(s->cred, buf, &cbsize); + SetDlgItemText(hwnd, IDC_PPK5_NAME, buf); + + cbsize = sizeof(buf); + kcdb_cred_get_attr_string(s->cred, KCDB_ATTR_ISSUE, buf, &cbsize, 0); + SetDlgItemText(hwnd, IDC_PPK5_ISSUE, buf); + + cbsize = sizeof(buf); + kcdb_cred_get_attr_string(s->cred, KCDB_ATTR_EXPIRE, buf, &cbsize, 0); + SetDlgItemText(hwnd, IDC_PPK5_VALID, buf); + + cbsize = sizeof(buf); + kcdb_cred_get_attr_string(s->cred, KCDB_ATTR_RENEW_EXPIRE, buf, &cbsize, 0); + SetDlgItemText(hwnd, IDC_PPK5_RENEW, buf); + + /*TODO: select other properties */ + } else { + /*TODO: select properties */ + } + } + return FALSE; + } + + return FALSE; +} + +void k5_pp_begin(khui_property_sheet * s) +{ + PROPSHEETPAGE *p; + + if(s->credtype == credtype_id_krb5) { + p = malloc(sizeof(*p)); + ZeroMemory(p, sizeof(*p)); + + p->dwSize = sizeof(*p); + p->dwFlags = 0; + p->hInstance = hResModule; + p->pszTemplate = (s->cred)? MAKEINTRESOURCE(IDD_PP_KRB5C): MAKEINTRESOURCE(IDD_PP_KRB5); + p->pfnDlgProc = krb5_pp_proc; + p->lParam = (LPARAM) s; + khui_ps_add_page(s, credtype_id_krb5, 0, p, NULL); + } +} + +void k5_pp_end(khui_property_sheet * s) +{ + khui_property_page * p = NULL; + + khui_ps_find_page(s, credtype_id_krb5, &p); + if(p) { + if(p->p_page) + free(p->p_page); + p->p_page = NULL; + } +} + diff --git a/src/windows/identity/plugins/krb5/krb5util.c b/src/windows/identity/plugins/krb5/krb5util.c new file mode 100644 index 000000000..b892531af --- /dev/null +++ b/src/windows/identity/plugins/krb5/krb5util.c @@ -0,0 +1,1362 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include "leashdll.h" +#include +#include +#include + +#include +#include "leasherr.h" +#include "leash-int.h" +#include "leashids.h" + +#include + +#include +#include "reminder.h" + +static char FAR *err_context; + +char KRB_HelpFile[_MAX_PATH] = HELPFILE; + +#define LEN 64 /* Maximum Hostname Length */ + +#define LIFE DEFAULT_TKT_LIFE /* lifetime of ticket in 5-minute units */ + +char * +short_date(dp) + long *dp; +{ + register char *cp; + cp = ctime(dp) + 4; // skip day of week + // cp[15] = '\0'; + cp[12] = '\0'; // Don't display seconds + return (cp); +} + + +static +char* +clean_string( + char* s + ) +{ + char* p = s; + char* b = s; + + if (!s) return s; + + for (p = s; *p; p++) { + switch (*p) { + case '\007': + /* Add more cases here */ + break; + default: + *b = *p; + b++; + } + } + *b = *p; + return s; +} + +static +int +leash_error_message( + const char *error, + int rcL, + int rc4, + int rc5, + int rcA, + char* result_string, + int displayMB + ) +{ + char message[2048]; + char *p = message; + int size = sizeof(message); + int n; + + // XXX: ignore AFS for now. + + if (!rc5 && !rc4 && !rcL) + return 0; + + n = _snprintf(p, size, "%s\n\n", error); + p += n; + size -= n; + + if (rc5 && !result_string) + { + n = _snprintf(p, size, + "Kerberos 5: %s (error %ld)\n", + perror_message(rc5), + rc5 & 255 // XXX: & 255??!!! + ); + p += n; + size -= n; + } + if (rc4 && !result_string) + { + char buffer[1024]; + n = _snprintf(p, size, + "Kerberos 4: %s\n", + err_describe(buffer, rc4) + ); + p += n; + size -= n; + } + if (rcL) + { + char buffer[1024]; + n = _snprintf(p, size, + "\n%s\n", + err_describe(buffer, rcL) + ); + p += n; + size -= n; + } + if (result_string) + { + n = _snprintf(p, size, + "%s\n", + result_string); + p += n; + size -= n; + } + if ( displayMB ) + MessageBox(NULL, message, "Leash", MB_OK | MB_ICONERROR | MB_TASKMODAL | + MB_SETFOREGROUND); + + if (rc5) return rc5; + if (rc4) return rc4; + if (rcL) return rcL; + return 0; +} + + +static +char * +make_postfix( + const char * base, + const char * postfix, + char ** rcopy + ) +{ + int base_size; + int ret_size; + char * copy = 0; + char * ret = 0; + + base_size = strlen(base) + 1; + ret_size = base_size + strlen(postfix) + 1; + copy = malloc(base_size); + ret = malloc(ret_size); + + if (!copy || !ret) + goto cleanup; + + strncpy(copy, base, base_size); + copy[base_size - 1] = 0; + + strncpy(ret, base, base_size); + strncpy(ret + (base_size - 1), postfix, ret_size - (base_size - 1)); + ret[ret_size - 1] = 0; + + cleanup: + if (!copy || !ret) { + if (copy) + free(copy); + if (ret) + free(ret); + copy = ret = 0; + } + // INVARIANT: (ret ==> copy) && (copy ==> ret) + *rcopy = copy; + return ret; +} + +static +long +make_temp_cache_v4( + const char * postfix + ) +{ + static char * old_cache = 0; + + if (!pkrb_set_tkt_string || !ptkt_string || !pdest_tkt) + return 0; // XXX - is this appropriate? + + if (old_cache) { + pdest_tkt(); + pkrb_set_tkt_string(old_cache); + free(old_cache); + old_cache = 0; + } + + if (postfix) + { + char * tmp_cache = make_postfix(ptkt_string(), postfix, &old_cache); + + if (!tmp_cache) + return KFAILURE; + + pkrb_set_tkt_string(tmp_cache); + free(tmp_cache); + } + return 0; +} + +static +long +make_temp_cache_v5( + const char * postfix, + krb5_context * pctx + ) +{ + static krb5_context ctx = 0; + static char * old_cache = 0; + + // INVARIANT: old_cache ==> ctx && ctx ==> old_cache + + if (pctx) + *pctx = 0; + + if (!pkrb5_init_context || !pkrb5_free_context || !pkrb5_cc_resolve || + !pkrb5_cc_default_name || !pkrb5_cc_set_default_name) + return 0; + + if (old_cache) { + krb5_ccache cc = 0; + if (!pkrb5_cc_resolve(ctx, pkrb5_cc_default_name(ctx), &cc)) + pkrb5_cc_destroy(ctx, cc); + pkrb5_cc_set_default_name(ctx, old_cache); + free(old_cache); + old_cache = 0; + } + if (ctx) { + pkrb5_free_context(ctx); + ctx = 0; + } + + if (postfix) + { + char * tmp_cache = 0; + krb5_error_code rc = 0; + + rc = pkrb5_init_context(&ctx); + if (rc) goto cleanup; + + tmp_cache = make_postfix(pkrb5_cc_default_name(ctx), postfix, + &old_cache); + + if (!tmp_cache) { + rc = ENOMEM; + goto cleanup; + } + + rc = pkrb5_cc_set_default_name(ctx, tmp_cache); + + cleanup: + if (rc && ctx) { + pkrb5_free_context(ctx); + ctx = 0; + } + if (tmp_cache) + free(tmp_cache); + if (pctx) + *pctx = ctx; + return rc; + } + return 0; +} + +long +Leash_checkpwd( + char *principal, + char *password + ) +{ + return Leash_int_checkpwd(principal, password, 0); +} + +long +Leash_int_checkpwd( + char * principal, + char * password, + int displayErrors + ) +{ + long rc = 0; + krb5_context ctx = 0; // statically allocated in make_temp_cache_v5 + // XXX - we ignore errors in make_temp_cache_v? This is BAD!!! + make_temp_cache_v4("_checkpwd"); + make_temp_cache_v5("_checkpwd", &ctx); + rc = Leash_int_kinit_ex( ctx, 0, + principal, password, 0, 0, 0, 0, + Leash_get_default_noaddresses(), + Leash_get_default_publicip(), + displayErrors + ); + make_temp_cache_v4(0); + make_temp_cache_v5(0, &ctx); + return rc; +} + +static +long +Leash_changepwd_v5(char * principal, + char * password, + char * newpassword, + char** error_str) +{ + krb5_error_code rc = 0; + int result_code; + krb5_data result_code_string, result_string; + krb5_context context = 0; + krb5_principal princ = 0; + krb5_get_init_creds_opt opts; + krb5_creds creds; + DWORD addressless = 0; + + result_string.data = 0; + result_code_string.data = 0; + + if ( !pkrb5_init_context ) + goto cleanup; + + if (rc = pkrb5_init_context(&context)) { +#if 0 + com_err(argv[0], ret, "initializing kerberos library"); +#endif + goto cleanup; + } + + if (rc = pkrb5_parse_name(context, principal, &princ)) { +#if 0 + com_err(argv[0], ret, "parsing client name"); +#endif + goto cleanup; + } + + pkrb5_get_init_creds_opt_init(&opts); + pkrb5_get_init_creds_opt_set_tkt_life(&opts, 5*60); + pkrb5_get_init_creds_opt_set_renew_life(&opts, 0); + pkrb5_get_init_creds_opt_set_forwardable(&opts, 0); + pkrb5_get_init_creds_opt_set_proxiable(&opts, 0); + + addressless = Leash_get_default_noaddresses(); + if (addressless) + pkrb5_get_init_creds_opt_set_address_list(&opts,NULL); + + + if (rc = pkrb5_get_init_creds_password(context, &creds, princ, password, + 0, 0, 0, "kadmin/changepw", &opts)) { + if (rc == KRB5KRB_AP_ERR_BAD_INTEGRITY) { +#if 0 + com_err(argv[0], 0, + "Password incorrect while getting initial ticket"); +#endif + } + else { +#if 0 + com_err(argv[0], ret, "getting initial ticket"); +#endif + } + goto cleanup; + } + + if (rc = pkrb5_change_password(context, &creds, newpassword, + &result_code, &result_code_string, + &result_string)) { +#if 0 + com_err(argv[0], ret, "changing password"); +#endif + goto cleanup; + } + + if (result_code) { + int len = result_code_string.length + + (result_string.length ? (sizeof(": ") - 1) : 0) + + result_string.length; + if (len && error_str) { + *error_str = malloc(len + 1); + if (*error_str) + _snprintf(*error_str, len + 1, + "%.*s%s%.*s", + result_code_string.length, result_code_string.data, + result_string.length?": ":"", + result_string.length, result_string.data); + } + rc = result_code; + goto cleanup; + } + + cleanup: + if (result_string.data) + pkrb5_free_data_contents(context, &result_string); + + if (result_code_string.data) + pkrb5_free_data_contents(context, &result_code_string); + + if (princ) + pkrb5_free_principal(context, princ); + + if (context) + pkrb5_free_context(context); + + return rc; +} + +static +long +Leash_changepwd_v4( + char * principal, + char * password, + char * newpassword, + char** error_str + ) +{ + long k_errno; + + if (!pkrb_set_tkt_string || !ptkt_string || !pkadm_change_your_password || + !pdest_tkt) + return KFAILURE; + + k_errno = make_temp_cache_v4("_chgpwd"); + if (k_errno) return k_errno; + k_errno = pkadm_change_your_password(principal, password, newpassword, + error_str); + make_temp_cache_v4(0); + return k_errno; +} + +/* + * Leash_changepwd + * + * Try to change the password using one of krb5 or krb4 -- whichever one + * works. We return ok on the first one that works. + */ +long +Leash_changepwd( + char * principal, + char * password, + char * newpassword, + char** result_string + ) +{ + return Leash_int_changepwd(principal, password, newpassword, result_string, 0); +} + +long +Leash_int_changepwd( + char * principal, + char * password, + char * newpassword, + char** result_string, + int displayErrors + ) +{ + char* v5_error_str = 0; + char* v4_error_str = 0; + char* error_str = 0; + int rc4 = 0; + int rc5 = 0; + int rc = 0; + if (hKrb5) + rc = rc5 = Leash_changepwd_v5(principal, password, newpassword, + &v5_error_str); + if (hKrb4 && + Leash_get_default_use_krb4() && + (!hKrb5 || rc5)) + rc = rc4 = Leash_changepwd_v4(principal, password, newpassword, + &v4_error_str); + if (!rc) + return 0; + if (v5_error_str || v4_error_str) { + int len = 0; + char v5_prefix[] = "Kerberos 5: "; + char sep[] = "\n"; + char v4_prefix[] = "Kerberos 4: "; + + clean_string(v5_error_str); + clean_string(v4_error_str); + + if (v5_error_str) + len += sizeof(sep) + sizeof(v5_prefix) + strlen(v5_error_str) + + sizeof(sep); + if (v4_error_str) + len += sizeof(sep) + sizeof(v4_prefix) + strlen(v4_error_str) + + sizeof(sep); + error_str = malloc(len + 1); + if (error_str) { + char* p = error_str; + int size = len + 1; + int n; + if (v5_error_str) { + n = _snprintf(p, size, "%s%s%s%s", + sep, v5_prefix, v5_error_str, sep); + p += n; + size -= n; + } + if (v4_error_str) { + n = _snprintf(p, size, "%s%s%s%s", + sep, v4_prefix, v4_error_str, sep); + p += n; + size -= n; + } + if (result_string) + *result_string = error_str; + } + } + return leash_error_message("Error while changing password.", + rc4, rc4, rc5, 0, error_str, + displayErrors + ); +} + +int (*Lcom_err)(LPSTR,long,LPSTR,...); +LPSTR (*Lerror_message)(long); +LPSTR (*Lerror_table_name)(long); + + +long +Leash_kinit( + char * principal, + char * password, + int lifetime + ) +{ + return Leash_int_kinit_ex( 0, 0, + principal, + password, + lifetime, + Leash_get_default_forwardable(), + Leash_get_default_proxiable(), + Leash_get_default_renew_till(), + Leash_get_default_noaddresses(), + Leash_get_default_publicip(), + 0 + ); +} + +long +Leash_kinit_ex( + char * principal, + char * password, + int lifetime, + int forwardable, + int proxiable, + int renew_life, + int addressless, + unsigned long publicip + ) +{ + return Leash_int_kinit_ex( 0, /* krb5 context */ + 0, /* parent window */ + principal, + password, + lifetime, + forwardable, + proxiable, + renew_life, + addressless, + publicip, + 0 + ); +} + +long +Leash_int_kinit_ex( + krb5_context ctx, + HWND hParent, + char * principal, + char * password, + int lifetime, + int forwardable, + int proxiable, + int renew_life, + int addressless, + unsigned long publicip, + int displayErrors + ) +{ + LPCSTR functionName; + char aname[ANAME_SZ]; + char inst[INST_SZ]; + char realm[REALM_SZ]; + char first_part[256]; + char second_part[256]; + char temp[1024]; + int count; + int i; + int rc4 = 0; + int rc5 = 0; + int rcA = 0; + int rcL = 0; + + if (lifetime < 5) + lifetime = 1; + else + lifetime /= 5; + + if (renew_life > 0 && renew_life < 5) + renew_life = 1; + else + renew_life /= 5; + + /* This should be changed if the maximum ticket lifetime */ + /* changes */ + + if (lifetime > 255) + lifetime = 255; + + err_context = "parsing principal"; + + memset(temp, '\0', sizeof(temp)); + memset(inst, '\0', sizeof(inst)); + memset(realm, '\0', sizeof(realm)); + memset(first_part, '\0', sizeof(first_part)); + memset(second_part, '\0', sizeof(second_part)); + + sscanf(principal, "%[/0-9a-zA-Z._-]@%[/0-9a-zA-Z._-]", first_part, second_part); + strcpy(temp, first_part); + strcpy(realm, second_part); + memset(first_part, '\0', sizeof(first_part)); + memset(second_part, '\0', sizeof(second_part)); + if (sscanf(temp, "%[@0-9a-zA-Z._-]/%[@0-9a-zA-Z._-]", first_part, second_part) == 2) + { + strcpy(aname, first_part); + strcpy(inst, second_part); + } + else + { + count = 0; + i = 0; + for (i = 0; temp[i]; i++) + { + if (temp[i] == '.') + ++count; + } + if (count > 1) + { + strcpy(aname, temp); + } + else + { + if (pkname_parse != NULL) + { + memset(first_part, '\0', sizeof(first_part)); + memset(second_part, '\0', sizeof(second_part)); + sscanf(temp, "%[@/0-9a-zA-Z_-].%[@/0-9a-zA-Z_-]", first_part, second_part); + strcpy(aname, first_part); + strcpy(inst, second_part); + } + else + { + strcpy(aname, temp); + } + } + } + + memset(temp, '\0', sizeof(temp)); + strcpy(temp, aname); + if (strlen(inst) != 0) + { + strcat(temp, "/"); + strcat(temp, inst); + } + if (strlen(realm) != 0) + { + strcat(temp, "@"); + strcat(temp, realm); + } + + rc5 = Leash_krb5_kinit(ctx, hParent, + temp, password, lifetime, + forwardable, + proxiable, + renew_life, + addressless, + publicip + ); + if ( Leash_get_default_use_krb4() ) { + if ( !rc5 ) { + if (!Leash_convert524(ctx)) + rc4 = KFAILURE; + } else { + if (pkname_parse == NULL) + { + goto cleanup; + } + + err_context = "getting realm"; + if (!*realm && (rc4 = (int)(*pkrb_get_lrealm)(realm, 1))) + { + functionName = "krb_get_lrealm()"; + rcL = LSH_FAILEDREALM; + goto cleanup; + } + + err_context = "checking principal"; + if ((!*aname) || (!(rc4 = (int)(*pk_isname)(aname)))) + { + functionName = "krb_get_lrealm()"; + rcL = LSH_INVPRINCIPAL; + goto cleanup; + } + + /* optional instance */ + if (!(rc4 = (int)(*pk_isinst)(inst))) + { + functionName = "k_isinst()"; + rcL = LSH_INVINSTANCE; + goto cleanup; + } + + if (!(rc4 = (int)(*pk_isrealm)(realm))) + { + functionName = "k_isrealm()"; + rcL = LSH_INVREALM; + goto cleanup; + } + + err_context = "fetching ticket"; + rc4 = (*pkrb_get_pw_in_tkt)(aname, inst, realm, "krbtgt", realm, + lifetime, password); + if (rc4) /* XXX: do we want: && (rc != NO_TKT_FIL) as well? */ + { + functionName = "krb_get_pw_in_tkt()"; + rcL = KRBERR(rc4); + goto cleanup; + } + } + } + +#ifndef NO_AFS + if ( !rc5 || (Leash_get_default_use_krb4() && !rc4) ) { + char c; + char *r; + char *t; + for ( r=realm, t=temp; c=*r; r++,t++ ) + *t = isupper(c) ? tolower(c) : c; + *t = '\0'; + + rcA = Leash_afs_klog("afs", temp, realm, lifetime); + if (rcA) + rcA = Leash_afs_klog("afs", "", realm, lifetime); + } +#endif /* NO_AFS */ + + cleanup: + return leash_error_message("Ticket initialization failed.", + rcL, (rc5 && rc4)?KRBERR(rc4):0, rc5, rcA, 0, + displayErrors); +} + +long FAR +Leash_renew(void) +{ + if ( hKrb5 && !LeashKRB5_renew() ) { + int lifetime; + lifetime = Leash_get_default_lifetime() / 5; + if (hKrb4 && Leash_get_default_use_krb4()) + Leash_convert524(0); +#ifndef NO_AFS + { + TicketList * list = NULL, * token; + afs_get_tokens(NULL,&list,NULL); + for ( token = list ; token ; token = token->next ) + Leash_afs_klog("afs", token->realm, "", lifetime); + not_an_API_LeashFreeTicketList(&list); + } +#endif /* NO_AFS */ + return 1; + } + return 0; +} + +static BOOL +GetSecurityLogonSessionData(PSECURITY_LOGON_SESSION_DATA * ppSessionData) +{ + NTSTATUS Status = 0; + HANDLE TokenHandle; + TOKEN_STATISTICS Stats; + DWORD ReqLen; + BOOL Success; + + if (!ppSessionData || !pLsaGetLogonSessionData) + return FALSE; + *ppSessionData = NULL; + + Success = OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &TokenHandle ); + if ( !Success ) + return FALSE; + + Success = GetTokenInformation( TokenHandle, TokenStatistics, &Stats, sizeof(TOKEN_STATISTICS), &ReqLen ); + CloseHandle( TokenHandle ); + if ( !Success ) + return FALSE; + + Status = pLsaGetLogonSessionData( &Stats.AuthenticationId, ppSessionData ); + if ( FAILED(Status) || !ppSessionData ) + return FALSE; + + return TRUE; +} + +// IsKerberosLogon() does not validate whether or not there are valid tickets in the +// cache. It validates whether or not it is reasonable to assume that if we +// attempted to retrieve valid tickets we could do so. Microsoft does not +// automatically renew expired tickets. Therefore, the cache could contain +// expired or invalid tickets. Microsoft also caches the user's password +// and will use it to retrieve new TGTs if the cache is empty and tickets +// are requested. + +static BOOL +IsKerberosLogon(VOID) +{ + PSECURITY_LOGON_SESSION_DATA pSessionData = NULL; + BOOL Success = FALSE; + + if ( GetSecurityLogonSessionData(&pSessionData) ) { + if ( pSessionData->AuthenticationPackage.Buffer ) { + WCHAR buffer[256]; + WCHAR *usBuffer; + int usLength; + + Success = FALSE; + usBuffer = (pSessionData->AuthenticationPackage).Buffer; + usLength = (pSessionData->AuthenticationPackage).Length; + if (usLength < 256) + { + lstrcpyn (buffer, usBuffer, usLength); + lstrcat (buffer,L""); + if ( !lstrcmp(L"Kerberos",buffer) ) + Success = TRUE; + } + } + pLsaFreeReturnBuffer(pSessionData); + } + return Success; +} + + +// This looks really ugly because it is. The result of IsKerberosLogon() +// does not prove whether or not there are Kerberos tickets available to +// be imported. Only the call to khm_krb5_ms2mit() which actually attempts +// to import tickets can do that. However, calling khm_krb5_ms2mit() can +// result in a TGS_REQ being sent to the KDC and since Leash_importable() +// is called quite often we want to avoid this if at all possible. +// Unfortunately, we have be shown at least one case in which the primary +// authentication package was not Kerberos and yet there were Kerberos +// tickets available. Therefore, if IsKerberosLogon() is not TRUE we +// must call khm_krb5_ms2mit() but we still do not want to call it in a +// tight loop so we cache the response and assume it won't change. +long FAR +Leash_importable(void) +{ + if ( IsKerberosLogon() ) + return TRUE; + else { + static int response = -1; + if (response == -1) { + response = khm_krb5_ms2mit(0); + } + return response; + } +} + +long FAR +Leash_import(void) +{ + if ( khm_krb5_ms2mit(1) ) { + int lifetime; + lifetime = Leash_get_default_lifetime() / 5; + if (hKrb4 && Leash_get_default_use_krb4()) + Leash_convert524(0); +#ifndef NO_AFS + { + char c; + char *r; + char *t; + char cell[256]; + char realm[256]; + int i = 0; + int rcA = 0; + + krb5_context ctx = 0; + krb5_error_code code = 0; + krb5_ccache cc = 0; + krb5_principal me = 0; + + if ( !pkrb5_init_context ) + goto cleanup; + + code = pkrb5_init_context(&ctx); + if (code) goto cleanup; + + code = pkrb5_cc_default(ctx, &cc); + if (code) goto cleanup; + + if (code = pkrb5_cc_get_principal(ctx, cc, &me)) + goto cleanup; + + for ( r=realm, t=cell, i=0; ilength; r++,t++,i++ ) { + c = krb5_princ_realm(ctx, me)->data[i]; + *r = c; + *t = isupper(c) ? tolower(c) : c; + } + *r = *t = '\0'; + + rcA = Leash_afs_klog("afs", cell, realm, lifetime); + if (rcA) + rcA = Leash_afs_klog("afs", "", realm, lifetime); + + cleanup: + if (me) + pkrb5_free_principal(ctx, me); + if (cc) + pkrb5_cc_close(ctx, cc); + if (ctx) + pkrb5_free_context(ctx); + } +#endif /* NO_AFS */ + return 1; + } + return 0; +} + +long +Leash_kdestroy(void) +{ + int k_errno; + + Leash_afs_unlog(); + khm_krb5_destroy_identity(NULL); + + if (pdest_tkt != NULL) + { + k_errno = (*pdest_tkt)(); + if (k_errno && (k_errno != RET_TKFIL)) + return KRBERR(k_errno); + } + + return 0; +} + +int com_addr(void) +{ + long ipAddr; + char loc_addr[ADDR_SZ]; + CREDENTIALS cred; + char service[40]; + char instance[40]; +// char addr[40]; + char realm[40]; + struct in_addr LocAddr; + int k_errno; + + if (pkrb_get_cred == NULL) + return(KSUCCESS); + + k_errno = (*pkrb_get_cred)(service,instance,realm,&cred); + if (k_errno) + return KRBERR(k_errno); + + + while(1) { + ipAddr = (*pLocalHostAddr)(); + LocAddr.s_addr = ipAddr; + strcpy(loc_addr,inet_ntoa(LocAddr)); + if ( strcmp(cred.address,loc_addr) != 0) { + Leash_kdestroy (); + break; + } + break; + } // while() + return 0; +} + +long FAR +not_an_API_LeashFreeTicketList(TicketList** ticketList) +{ + TicketList* tempList = *ticketList, *killList; + + //if (tempList == NULL) + //return -1; + + while (tempList) + { + killList = tempList; + + tempList = (TicketList*)tempList->next; + free(killList->theTicket); + if (killList->tktEncType) + free(killList->tktEncType); + if (killList->keyEncType) + free(killList->keyEncType); + if (killList->addrCount) { + int n; + for ( n=0; naddrCount; n++) { + if (killList->addrList[n]) + free(killList->addrList[n]); + } + } + if (killList->addrList) + free(killList->addrList); + if (killList->name) + free(killList->name); + if (killList->inst) + free(killList->inst); + if (killList->realm) + free(killList->realm); + free(killList); + } + + *ticketList = NULL; + return 0; +} + + +long FAR Leash_klist(HWND hlist, TICKETINFO FAR *ticketinfo) +{ + // Don't think this function will be used anymore - ADL 5-15-99 + // Old fucntion to put tickets in a listbox control + // Use function "not_an_API_LeashKRB4GetTickets()" instead! + char pname[ANAME_SZ]; + char pinst[INST_SZ]; + char prealm[REALM_SZ]; + char buf[MAX_K_NAME_SZ+40]; + LPSTR cp; + long expdate; + int k_errno; + CREDENTIALS c; + int newtickets = 0; + int open = 0; + + /* + * Since krb_get_tf_realm will return a ticket_file error, + * we will call tf_init and tf_close first to filter out + * things like no ticket file. Otherwise, the error that + * the user would see would be + * klist: can't find realm of ticket file: No ticket file (tf_util) + * instead of + * klist: No ticket file (tf_util) + */ + if (ptf_init == NULL) + return(KSUCCESS); + + if (hlist) + { + SendMessage(hlist, WM_SETREDRAW, FALSE, 0L); + SendMessage(hlist, LB_RESETCONTENT, 0, 0L); + } + com_addr(); + newtickets = NO_TICKETS; + + err_context = (LPSTR)"tktf1"; + + /* Open ticket file */ + if (k_errno = (*ptf_init)((*ptkt_string)(), R_TKT_FIL)) + { + goto cleanup; + } + /* Close ticket file */ + (void) (*ptf_close)(); + /* + * We must find the realm of the ticket file here before calling + * tf_init because since the realm of the ticket file is not + * really stored in the principal section of the file, the + * routine we use must itself call tf_init and tf_close. + */ + err_context = "tf realm"; + if ((k_errno = (*pkrb_get_tf_realm)((*ptkt_string)(), prealm)) != KSUCCESS) + { + goto cleanup; + } + /* Open ticket file */ + err_context = "tf init"; + if (k_errno = (*ptf_init)((*ptkt_string)(), R_TKT_FIL)) + { + goto cleanup; + } + + open = 1; + err_context = "tf pname"; + /* Get principal name and instance */ + if ((k_errno = (*ptf_get_pname)(pname)) || (k_errno = (*ptf_get_pinst)(pinst))) + { + goto cleanup; + } + + /* + * You may think that this is the obvious place to get the + * realm of the ticket file, but it can't be done here as the + * routine to do this must open the ticket file. This is why + * it was done before tf_init. + */ + + wsprintf((LPSTR)ticketinfo->principal,"%s%s%s%s%s", (LPSTR)pname, + (LPSTR)(pinst[0] ? "." : ""), (LPSTR)pinst, + (LPSTR)(prealm[0] ? "@" : ""), (LPSTR)prealm); + newtickets = GOOD_TICKETS; + + err_context = "tf cred"; + while ((k_errno = (*ptf_get_cred)(&c)) == KSUCCESS) + { + expdate = c.issue_date + c.lifetime * 5L * 60L; + + if (!lstrcmp((LPSTR)c.service, (LPSTR)TICKET_GRANTING_TICKET) && !lstrcmp((LPSTR)c.instance, (LPSTR)prealm)) + { + ticketinfo->issue_date = c.issue_date; + ticketinfo->lifetime = c.lifetime * 5L * 60L; + ticketinfo->renew_till = 0; + } + + cp = (LPSTR)buf; + lstrcpy(cp, (LPSTR)short_date(&c.issue_date)); + cp += lstrlen(cp); + wsprintf(cp,"\t%s\t%s%s%s%s%s", + (LPSTR)short_date(&expdate), (LPSTR)c.service, + (LPSTR)(c.instance[0] ? "." : ""), + (LPSTR)c.instance, (LPSTR)(c.realm[0] ? "@" : ""), + (LPSTR) c.realm); + if (hlist) + SendMessage(hlist, LB_ADDSTRING, 0, (LONG)(LPSTR)buf); + } /* WHILE */ + +cleanup: + + if (open) + (*ptf_close)(); /* close ticket file */ + + if (hlist) + { + SendMessage(hlist, WM_SETREDRAW, TRUE, 0L); + InvalidateRect(hlist, NULL, TRUE); + UpdateWindow(hlist); + } + if (k_errno == EOF) + k_errno = 0; + + /* XXX the if statement directly below was inserted to eliminate + an error 20 on Leash startup. The error occurs from an error + number thrown from krb_get_tf_realm. We believe this change + does not eliminate other errors, but it may. */ + + if (k_errno == RET_NOTKT) + k_errno = 0; + + ticketinfo->btickets = newtickets; + if (k_errno != 0) + return KRBERR(k_errno); + return 0; +} + + + +static BOOL CALLBACK +EnumChildProc(HWND hwnd, LPARAM lParam) +{ + HWND * h = (HWND *)lParam; + *h = hwnd; + return FALSE; +} + + +static HWND +FindFirstChildWindow(HWND parent) +{ + HWND hFirstChild = 0; + EnumChildWindows(parent, EnumChildProc, (LPARAM) &hFirstChild); + return hFirstChild; +} + +void FAR +not_an_API_Leash_AcquireInitialTicketsIfNeeded(krb5_context context, krb5_principal desiredKrb5Principal) +{ + krb5_error_code err; + LSH_DLGINFO_EX dlginfo; + HGLOBAL hData; + HWND hLeash; + HWND hForeground; + char *desiredName = 0; + char *desiredRealm = 0; + char *p; + TicketList * list = NULL; + TICKETINFO ticketinfo; + krb5_context ctx; + char newenv[256]; + char * env = 0; + DWORD dwMsLsaImport = Leash_get_default_mslsa_import(); + + char loginenv[16]; + BOOL prompt; + + GetEnvironmentVariable("KERBEROSLOGIN_NEVER_PROMPT", loginenv, sizeof(loginenv)); + prompt = (GetLastError() == ERROR_ENVVAR_NOT_FOUND); + + if ( !prompt || !pkrb5_init_context ) + return; + + ctx = context; + env = getenv("KRB5CCNAME"); + if ( !env && context ) { + sprintf(newenv,"KRB5CCNAME=%s",pkrb5_cc_default_name(ctx)); + env = (char *)putenv(newenv); + } + + not_an_API_LeashKRB5GetTickets(&ticketinfo,&list,&ctx); + not_an_API_LeashFreeTicketList(&list); + + if ( ticketinfo.btickets != GOOD_TICKETS && + Leash_get_default_mslsa_import() && Leash_importable() ) { + // We have the option of importing tickets from the MSLSA + // but should we? Do the tickets in the MSLSA cache belong + // to the default realm used by Leash? If so, import. + int import = 0; + + if ( dwMsLsaImport == 1 ) { /* always import */ + import = 1; + } else if ( dwMsLsaImport == 2 ) { /* import when realms match */ + krb5_error_code code; + krb5_ccache mslsa_ccache=0; + krb5_principal princ = 0; + char ms_realm[128] = "", *def_realm = 0, *r; + int i; + + if (code = pkrb5_cc_resolve(ctx, "MSLSA:", &mslsa_ccache)) + goto cleanup; + + if (code = pkrb5_cc_get_principal(ctx, mslsa_ccache, &princ)) + goto cleanup; + + for ( r=ms_realm, i=0; ilength; r++, i++ ) { + *r = krb5_princ_realm(ctx, princ)->data[i]; + } + *r = '\0'; + + if (code = pkrb5_get_default_realm(ctx, &def_realm)) + goto cleanup; + + import = !strcmp(def_realm, ms_realm); + + cleanup: + if (def_realm) + pkrb5_free_default_realm(ctx, def_realm); + + if (princ) + pkrb5_free_principal(ctx, princ); + + if (mslsa_ccache) + pkrb5_cc_close(ctx, mslsa_ccache); + } + + if ( import ) { + Leash_import(); + + not_an_API_LeashKRB5GetTickets(&ticketinfo,&list,&ctx); + not_an_API_LeashFreeTicketList(&list); + } + } + + if ( ticketinfo.btickets != GOOD_TICKETS ) + { + /* do we want a specific client principal? */ + if (desiredKrb5Principal != NULL) { + err = pkrb5_unparse_name (ctx, desiredKrb5Principal, &desiredName); + if (!err) { + dlginfo.username = desiredName; + for (p = desiredName; *p && *p != '@'; p++); + if ( *p == '@' ) { + *p = '\0'; + desiredRealm = dlginfo.realm = ++p; + } + } + } + +#ifdef COMMENT + memset(&dlginfo, 0, sizeof(LSH_DLGINFO_EX)); + dlginfo.size = sizeof(LSH_DLGINFO_EX); + dlginfo.dlgtype = DLGTYPE_PASSWD; + dlginfo.title = "Obtain Kerberos Ticket Getting Tickets"; + dlginfo.use_defaults = 1; + + err = Leash_kinit_dlg_ex(NULL, &dlginfo); +#else + /* construct a marshalling of data + * <principal><realm> + * then send to Leash + */ + + hData = GlobalAlloc( GHND, 4096 ); + hForeground = GetForegroundWindow(); + hLeash = FindWindow("LEASH.0WNDCLASS", NULL); + SetForegroundWindow(hLeash); + hLeash = FindFirstChildWindow(hLeash); + if ( hData && hLeash ) { + char * strs = GlobalLock( hData ); + if ( strs ) { + strcpy(strs, "Obtain Kerberos Ticket Getting Tickets"); + strs += strlen(strs) + 1; + if ( desiredName ) { + strcpy(strs, desiredName); + strs += strlen(strs) + 1; + if (desiredRealm) { + strcpy(strs, desiredRealm); + strs += strlen(strs) + 1; + } + } else { + *strs = 0; + strs++; + *strs = 0; + strs++; + } + + GlobalUnlock( hData ); + SendMessage(hLeash, 32809, 0, (LPARAM) hData); + } + + GlobalFree( hData ); + } + SetForegroundWindow(hForeground); +#endif + if (desiredName != NULL) + pkrb5_free_unparsed_name(ctx, desiredName); + } + + if ( !env && context ) + putenv("KRB5CCNAME="); + + if ( !context ) + pkrb5_free_context(ctx); +} diff --git a/src/windows/identity/plugins/krb5/krbconfig.csv b/src/windows/identity/plugins/krb5/krbconfig.csv new file mode 100644 index 000000000..c577eec3b --- /dev/null +++ b/src/windows/identity/plugins/krb5/krbconfig.csv @@ -0,0 +1,34 @@ +Name,Type,Value,Description +Krb5Cred,KC_SPACE,0,Kerberos V Credentials Provider + Module,KC_STRING,MITKrb5, + Description,KC_STRING,Kerberos V Credentials Provider, + Type,KC_INT32,1, + Flags,KC_INT32,0, + Parameters,KC_SPACE,0,Parameters for KrbCred + CreateMissingConfig,KC_INT32,0,Create missing configuration files + MsLsaImport,KC_INT32,2,Automatically import MSLSA credentials + AutoRenewTickets,KC_INT32,1,Automatically renew expiring tickets + DefaultLifetime,KC_INT32,36000,Default ticket lifetime + MaxLifetime,KC_INT32,86400,Maximum lifetime + MinLifetime,KC_INT32,60,Minimum lifetime + Forwardable,KC_INT32,1,Obtain forwardable tickets (boolean) + Proxiable,KC_INT32,0,Obtain proxiable tickets (boolean) + Addressless,KC_INT32,1,Obtain addressless tickets (boolean) + Renewable,KC_INT32,1,Obtain renewable tickets (boolean) + DefaultRenewLifetime,KC_INT32,604800,Default renewable lifetime + MaxRenewLifetime,KC_INT32,2592000,Maximum renewable lifetime + MinRenewLifetime,KC_INT32,60,Maximum renewable lifetime + LRURealms,KC_STRING,, + LRUPrincipals,KC_STRING,, + PromptCache,KC_SPACE,0,Cache of prompts (only per identity) + Name,KC_STRING,, + Banner,KC_STRING,, + PromptCount,KC_INT32,0, + (n),KC_SPACE,0,Parameters for each prompt + Prompt,KC_STRING,, + Type,KC_INT32,0, + Flags,KC_INT32,0, + (n),KC_ENDSPACE,0, + PromptCache,KC_ENDSPACE,0, + Parameters,KC_ENDSPACE,0, +Krb5Cred,KC_ENDSPACE,0, diff --git a/src/windows/identity/plugins/krb5/krbcred.h b/src/windows/identity/plugins/krb5/krbcred.h new file mode 100644 index 000000000..08978f11f --- /dev/null +++ b/src/windows/identity/plugins/krb5/krbcred.h @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KRBAFSCRED_H +#define __KHIMAIRA_KRBAFSCRED_H + +#include<windows.h> + +/* While we generally pull resources out of hResModule, the message + strings for all the languages are kept in the main DLL. */ +#define KHERR_HMODULE hInstance +#define KHERR_FACILITY k5_facility +#define KHERR_FACILITY_ID 64 + +#include<khdefs.h> +#include<kcreddb.h> +#include<kmm.h> +#include<kconfig.h> +#include<khuidefs.h> +#include<kherr.h> + +#include<krb5funcs.h> +#include<krb5common.h> +#include<errorfuncs.h> +#include<dynimport.h> + +#include<langres.h> +#include<datarep.h> +#include<krb5_msgs.h> + +#define TYPENAME_ENCTYPE L"EncType" +#define TYPENAME_ADDR_LIST L"AddrList" +#define TYPENAME_KRB5_FLAGS L"Krb5Flags" + +#define ATTRNAME_KEY_ENCTYPE L"KeyEncType" +#define ATTRNAME_TKT_ENCTYPE L"TktEncType" +#define ATTRNAME_ADDR_LIST L"AddrList" +#define ATTRNAME_KRB5_FLAGS L"Krb5Flags" +#define ATTRNAME_KRB5_CCNAME L"Krb5CCName" + +void init_krb(); +void exit_krb(); +KHMEXP khm_int32 KHMAPI init_module(kmm_module h_module); +KHMEXP khm_int32 KHMAPI exit_module(kmm_module h_module); + +/* globals */ +extern kmm_module h_khModule; +extern HMODULE hResModule; +extern HINSTANCE hInstance; +extern const wchar_t * k5_facility; + +extern khm_int32 type_id_enctype; +extern khm_int32 type_id_addr_list; +extern khm_int32 type_id_krb5_flags; + +extern khm_int32 attr_id_key_enctype; +extern khm_int32 attr_id_tkt_enctype; +extern khm_int32 attr_id_addr_list; +extern khm_int32 attr_id_krb5_flags; +extern khm_int32 attr_id_krb5_ccname; + +/* Configuration spaces */ +#define CSNAME_KRB5CRED L"Krb5Cred" +#define CSNAME_PARAMS L"Parameters" +#define CSNAME_PROMPTCACHE L"PromptCache" + +/* plugin constants */ +#define KRB5_PLUGIN_NAME L"Krb5Cred" + +#define KRB5_CREDTYPE_NAME L"Krb5Cred" + +extern khm_handle csp_plugins; +extern khm_handle csp_krbcred; +extern khm_handle csp_params; + +extern kconf_schema schema_krbconfig[]; + +/* other globals */ +extern khm_int32 credtype_id_krb5; + +extern khm_boolean krb5_initialized; + +extern khm_handle krb5_credset; + +extern khm_handle k5_sub; + +extern krb5_context k5_identpro_ctx; + +extern BOOL is_k5_identpro; + +/* plugin callbacks */ +khm_int32 KHMAPI k5_msg_callback(khm_int32 msg_type, khm_int32 msg_subtype, khm_ui_4 uparam, void * vparam); + +/* kinit fiber */ +typedef struct _fiber_job_t { + int command; + + khui_new_creds * nc; + khui_new_creds_by_type * nct; + HWND dialog; + + khm_handle identity; + char * principal; + char * password; + char * ccache; + krb5_deltat lifetime; + DWORD forwardable; + DWORD proxiable; + DWORD renewable; + krb5_deltat renew_life; + DWORD addressless; + DWORD publicIP; + + int code; + int state; + int prompt_set; + + BOOL null_password; +} fiber_job; + +extern fiber_job g_fjob; /* global fiber job object */ + +#define FIBER_CMD_KINIT 1 +#define FIBER_CMD_CANCEL 2 +#define FIBER_CMD_CONTINUE 3 + +#define FIBER_STATE_NONE 0 +#define FIBER_STATE_KINIT 1 + +void +k5_pp_begin(khui_property_sheet * s); + +void +k5_pp_end(khui_property_sheet * s); + +khm_int32 KHMAPI +k5_msg_cred_dialog(khm_int32 msg_type, + khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam); + +khm_int32 KHMAPI +k5_msg_ident(khm_int32 msg_type, + khm_int32 msg_subtype, + khm_ui_4 uparam, + void * vparam); + +int +k5_get_realm_from_nc(khui_new_creds * nc, + wchar_t * buf, + khm_size cch_buf); + +void +k5_register_config_panels(void); + +void +k5_unregister_config_panels(void); + +#endif diff --git a/src/windows/identity/plugins/krb5/lang/en_us/langres.rc b/src/windows/identity/plugins/krb5/lang/en_us/langres.rc new file mode 100644 index 000000000..087b93e47 --- /dev/null +++ b/src/windows/identity/plugins/krb5/lang/en_us/langres.rc @@ -0,0 +1,406 @@ +// Microsoft Visual C++ generated resource script. +// +#include "..\..\langres.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "..\\..\\langres.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_NC_KRB5 DIALOGEX 0, 0, 300, 166 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | + WS_CLIPCHILDREN +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Realm",IDC_STATIC,7,25,52,13 + COMBOBOX IDC_NCK5_REALM,60,25,233,17,CBS_DROPDOWN | + CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Specify &additional realms ...",IDC_NCK5_ADD_REALMS,181, + 43,112,16,BS_NOTIFY | WS_DISABLED + LTEXT "&Lifetime",IDC_STATIC,7,67,61,12 + EDITTEXT IDC_NCK5_LIFETIME_EDIT,85,67,107,12,ES_AUTOHSCROLL + CONTROL "&Renewable for",IDC_NCK5_RENEWABLE,"Button", + BS_AUTOCHECKBOX | BS_NOTIFY | WS_TABSTOP,7,87,64,12 + EDITTEXT IDC_NCK5_RENEW_EDIT,85,87,108,12,ES_AUTOHSCROLL + CONTROL "Can be &forwarded to other machines", + IDC_NCK5_FORWARDABLE,"Button",BS_AUTOCHECKBOX | + BS_NOTIFY | WS_TABSTOP,7,107,132,12 + CONTROL "Kerberos 5 Ticket Options",IDC_STATIC,"Static", + SS_LEFTNOWORDWRAP | SS_SUNKEN | WS_GROUP,7,7,286,11 +END + +IDD_PP_KRB5C DIALOGEX 0, 0, 235, 156 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Kerberos 5" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Name",IDC_STATIC,7,7,19,8 + LTEXT "Valid till",IDC_STATIC,7,39,24,8 + LTEXT "Renewable till",IDC_STATIC,7,55,45,12 + CONTROL "Renewable",IDC_PPK5_CRENEW,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,31,125,51,10 + CONTROL "Forwardable",IDC_PPK5_CFORWARD,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,91,125,56,10 + CONTROL "Proxiable",IDC_PPK5_CPROXY,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,156,125,45,10 + LTEXT "Issued on",IDC_STATIC,7,23,32,8 + GROUPBOX "Ticket flags",IDC_STATIC,7,108,221,41 + LTEXT "Static",IDC_PPK5_NAME,72,7,156,12,NOT WS_GROUP, + WS_EX_CLIENTEDGE + LTEXT "Static",IDC_PPK5_ISSUE,72,23,156,12,NOT WS_GROUP, + WS_EX_CLIENTEDGE + LTEXT "Static",IDC_PPK5_VALID,72,39,156,12,NOT WS_GROUP, + WS_EX_CLIENTEDGE + LTEXT "Static",IDC_PPK5_RENEW,72,55,156,12,NOT WS_GROUP, + WS_EX_CLIENTEDGE +END + +IDD_PP_KRB5 DIALOGEX 0, 0, 235, 156 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Kerberos 5" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Default realm",IDC_STATIC,7,7,44,8 + LTEXT "Default lifetime",IDC_STATIC,7,22,49,8 + LTEXT "Minimum lifetime",IDC_STATIC,7,37,52,8 + LTEXT "Maximum lifetime",IDC_STATIC,7,52,55,8 + LTEXT "Renewable lifetime",IDC_STATIC,7,67,61,8 + LTEXT "Min. Renewable lifetime",IDC_STATIC,7,82,76,8 + LTEXT "Max. Renewable lifetime",IDC_STATIC,7,97,79,8 + GROUPBOX "Default ticket flags",IDC_STATIC,7,113,221,36 + CONTROL "Proxiable",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,160,129,45,10 + CONTROL "Renewable",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,23,129,51,10 + CONTROL "Forwardable",IDC_CHECK5,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,89,129,56,10 + LTEXT "ATHENA.MIT.EDU",IDC_STATIC,95,7,133,11,0, + WS_EX_CLIENTEDGE + LTEXT "10 hours",IDC_STATIC,95,22,133,11,0,WS_EX_CLIENTEDGE + LTEXT "1 minute",IDC_STATIC,95,37,133,11,0,WS_EX_CLIENTEDGE + LTEXT "7 days",IDC_STATIC,95,52,133,11,0,WS_EX_CLIENTEDGE + LTEXT "7 days",IDC_STATIC,95,67,133,11,0,WS_EX_CLIENTEDGE + LTEXT "1 minute",IDC_STATIC,95,82,133,11,0,WS_EX_CLIENTEDGE + LTEXT "21 days",IDC_STATIC,95,97,133,11,0,WS_EX_CLIENTEDGE +END + +IDD_CONFIG DIALOGEX 0, 0, 255, 182 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Default Realm",IDC_CFG_LBL_REALM,13,9,46,8 + COMBOBOX IDC_CFG_DEFREALM,76,7,166,30,CBS_DROPDOWN | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Configure Realms ...",IDC_CFG_CFGREALMS,76,25,84,14 + GROUPBOX "Keberos Configuration File",IDC_CFG_CFGFILEGRP,7,57,241, + 48 + LTEXT "Location",IDC_CFG_LBL_CFGFILE,13,71,28,8 + EDITTEXT IDC_CFG_CFGFILE,76,68,119,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_CFG_BROWSE,198,68,44,14 + CONTROL "Create file if missing",IDC_CFG_CREATECONFIG,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,76,89,80,10 + GROUPBOX "Windows® Options",IDC_CFG_WINGRP,7,110,241,65 + LTEXT "Hostname",IDC_CFG_LBL_HOSTNAME,13,123,33,8 + EDITTEXT IDC_CFG_HOSTNAME,76,120,166,14,ES_AUTOHSCROLL | + ES_READONLY + LTEXT "Domain",IDC_CFG_LBL_DOMAIN,13,141,24,8 + EDITTEXT IDC_CFG_DOMAIN,76,138,166,14,ES_AUTOHSCROLL | + ES_READONLY + LTEXT "Import tickets",IDC_LBL_IMPORT,13,158,45,8 + COMBOBOX IDC_CFG_IMPORT,76,156,166,30,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP +END + +IDD_CFG_REALMS DIALOGEX 0, 0, 255, 182 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_CFG_REALMS,"SysListView32",LVS_ALIGNLEFT | + WS_BORDER | WS_TABSTOP,7,19,81,148 + GROUPBOX "Servers",IDC_CFG_SERVERSGRP,93,7,155,91 + GROUPBOX "Domain/Hostname mappings",IDC_CFG_DOMAINGRP,93,101,155, + 74 + CONTROL "",IDC_LIST3,"SysListView32",LVS_ALIGNLEFT | WS_BORDER | + WS_TABSTOP,99,19,143,72 + CONTROL "",IDC_LIST4,"SysListView32",LVS_ALIGNLEFT | WS_BORDER | + WS_TABSTOP,99,111,143,56 +END + +IDD_CFG_IDS_TAB DIALOGEX 0, 0, 235, 151 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Ticket lifetime",IDC_CFG_LBL_DEFLIFE,7,10,44,8 + EDITTEXT IDC_CFG_DEFLIFE,91,7,137,14,ES_AUTOHSCROLL + LTEXT "Ticket renewable lifetime",IDC_CFG_LBL_DEFRLIFE,7,29,80, + 8 + EDITTEXT IDC_CFG_DEFRLIFE,91,26,137,14,ES_AUTOHSCROLL + GROUPBOX "Ticket lifetime range",IDC_CFG_LIFEGRP,7,43,221,49 + LTEXT "Minimum",IDC_STATIC,13,56,28,8 + EDITTEXT IDC_CFG_LRNG_MIN,91,53,131,14,ES_AUTOHSCROLL + LTEXT "Maximum",IDC_STATIC,13,75,30,8 + EDITTEXT IDC_CFG_LRNG_MAX,91,72,131,14,ES_AUTOHSCROLL + GROUPBOX "Ticket renewable lifetime range",IDC_STATIC,7,95,221,49 + LTEXT "Minimum",IDC_STATIC,13,108,28,8 + EDITTEXT IDC_CFG_RLRNG_MIN,91,105,131,14,ES_AUTOHSCROLL + LTEXT "Maximum",IDC_STATIC,13,128,30,8 + EDITTEXT IDC_CFG_RLRNG_MAX,91,125,131,14,ES_AUTOHSCROLL +END + +IDD_CFG_ID_TAB DIALOGEX 0, 0, 235, 151 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Ticket lifetime",IDC_CFG_LBL_DEFLIFE,7,10,44,8 + EDITTEXT IDC_CFG_DEFLIFE,91,7,137,14,ES_AUTOHSCROLL + LTEXT "Ticket renewable lifetime",IDC_CFG_LBL_DEFRLIFE,7,29,80, + 8 + EDITTEXT IDC_CFG_DEFRLIFE,91,26,137,14,ES_AUTOHSCROLL + LTEXT "Credentials cache",IDC_STATIC,7,63,58,8 + EDITTEXT IDC_CFG_CCACHE,91,60,137,14,ES_AUTOHSCROLL +END + +IDD_NC_KRB5_PASSWORD DIALOGEX 0, 0, 300, 166 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "Kerberos 5 Change Password Options",IDC_STATIC,"Static", + SS_LEFTNOWORDWRAP | SS_SUNKEN | WS_GROUP,7,7,286,11 + LTEXT "Realm",IDC_STATIC,7,25,52,13 + COMBOBOX IDC_NCK5_REALM,60,25,233,17,CBS_DROPDOWN | + CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Specify &additional realms ...",IDC_NCK5_ADD_REALMS,181, + 43,112,16,BS_NOTIFY | WS_DISABLED +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_NC_KRB5, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 293 + TOPMARGIN, 7 + BOTTOMMARGIN, 159 + END + + IDD_PP_KRB5C, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 228 + TOPMARGIN, 7 + BOTTOMMARGIN, 149 + END + + IDD_PP_KRB5, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 228 + TOPMARGIN, 7 + BOTTOMMARGIN, 149 + END + + IDD_CONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 248 + VERTGUIDE, 13 + VERTGUIDE, 76 + VERTGUIDE, 242 + TOPMARGIN, 7 + BOTTOMMARGIN, 175 + END + + IDD_CFG_REALMS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 248 + VERTGUIDE, 93 + VERTGUIDE, 99 + VERTGUIDE, 242 + TOPMARGIN, 7 + BOTTOMMARGIN, 175 + HORZGUIDE, 19 + HORZGUIDE, 167 + END + + IDD_CFG_IDS_TAB, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 228 + VERTGUIDE, 13 + VERTGUIDE, 91 + VERTGUIDE, 222 + TOPMARGIN, 7 + BOTTOMMARGIN, 144 + END + + IDD_CFG_ID_TAB, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 228 + VERTGUIDE, 91 + TOPMARGIN, 7 + BOTTOMMARGIN, 144 + END + + IDD_NC_KRB5_PASSWORD, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 293 + TOPMARGIN, 7 + BOTTOMMARGIN, 159 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_UNK_ADDR_FMT "Unknown address type %d" + IDS_KRB5_CREDTEXT_0 "<p><a id=""SwitchPanel"" param=""Krb5Cred""><b>Krb5</b></a><tab>: Creds for realm %s</p>" + IDS_KRB5_CCNAME_SHORT_DESC "Krb5 CCache" + IDS_KEY_ENCTYPE_SHORT_DESC "Key EncType" + IDS_TKT_ENCTYPE_SHORT_DESC "Ticket EncType" + IDS_KEY_ENCTYPE_LONG_DESC "Session Key Encryption Type" + IDS_TKT_ENCTYPE_LONG_DESC "Ticket Encryption Type" + IDS_ADDR_LIST_SHORT_DESC "Addresses" + IDS_ADDR_LIST_LONG_DESC "Address List" + IDS_ETYPE_NULL "NULL" + IDS_ETYPE_DES_CBC_CRC "DES-CBC-CRC" +END + +STRINGTABLE +BEGIN + IDS_ETYPE_DES_CBC_MD4 "DES-CBC-MD4" + IDS_ETYPE_DES_CBC_MD5 "DES-CBC-MD5" + IDS_ETYPE_DES_CBC_RAW "DES-CBC-RAW" + IDS_ETYPE_DES3_CBC_SHA "DES3-CBC-SHA" + IDS_ETYPE_DES3_CBC_RAW "DES3-CBC-RAW" + IDS_ETYPE_DES_HMAC_SHA1 "DES-HMAC-SHA1" + IDS_ETYPE_DES3_CBC_SHA1 "DES3-CBC-SHA1" + IDS_ETYPE_AES128_CTS_HMAC_SHA1_96 "AES128_CTS-HMAC-SHA1_96" + IDS_ETYPE_AES256_CTS_HMAC_SHA1_96 "AES256_CTS-HMAC-SHA1_96" + IDS_ETYPE_ARCFOUR_HMAC "RC4-HMAC-NT" + IDS_ETYPE_ARCFOUR_HMAC_EXP "RC4-HMAC-NT-EXP" + IDS_ETYPE_UNKNOWN "(Unknown)" + IDS_ETYPE_LOCAL_DES3_HMAC_SHA1 "LOCAL-DES3-HMAC-SHA1" + IDS_ETYPE_LOCAL_RC4_MD4 "LOCAL-RC4-MD4" + IDS_KRB5_SHORT_DESC "Kerberos 5" + IDS_KRB5_LONG_DESC "Kerberos 5 tickets" +END + +STRINGTABLE +BEGIN + IDS_KRB4_SHORT_DESC "Kerberos 4" + IDS_KRB4_LONG_DESC "Kerberos 4 tickets" + IDS_KRB5_FLAGS_SHORT_DESC "Flags" + IDS_RENEW_TILL_SHORT_DESC "Renew Till" + IDS_RENEW_TILL_LONG_DESC "Renewable Till" + IDS_RENEW_FOR_SHORT_DESC "Renew for" + IDS_RENEW_FOR_LONG_DESC "Renewable for" + IDS_KRB5_CCNAME_LONG_DESC "Krb5 Primary Credentials Cache" + IDS_NC_USERNAME "Username" + IDS_NC_REALM "Realm" + IDS_KRB5_WARNING "Kerberos 5 Warning" + IDS_K5ERR_NAME_EXPIRED "<p><a id=""SwitchPanel"" param=""Krb5Cred""><b>Krb5</b></a><tab>: The selected principal name has expired.</p><p><tab> Please contact your system administrator.</p>" + IDS_K5ERR_KEY_EXPIRED "<p><a id=""SwitchPanel"" param=""Krb5Cred""><b>Krb5</b></a><tab>: The password for the selected identity has expired.</p><p><tab> Click <a id=""Krb5Cred:Passwd"">here</a> to change the password</p>" + IDS_KRB5_WARN_FMT "Kerberos 5: %s\n\n%s" + IDS_K5ERR_FMT "<p><a id=""SwitchPanel"" param=""Krb5Cred""><b>Krb5</b></a><tag>: %s</p>" + IDS_K5CFG_SHORT_DESC "Kerberos 5" +END + +STRINGTABLE +BEGIN + IDS_K5CFG_LONG_DESC "Kerberos 5 Configuration" + IDS_K5RLM_SHORT_DESC "Realms" + IDS_K5RLM_LONG_DESC "Kerberos Realm Configuration" + IDS_K5CFG_IDS_SHORT_DESC "Kerberos 5" + IDS_K5CFG_IDS_LONG_DESC "Kerberos 5 options for all identities" + IDS_K5CFG_ID_SHORT_DESC "Kerberos 5" + IDS_K5CFG_ID_LONG_DESC "Kerberos 5 options for this identity" + IDS_PLUGIN_DESC "Kerberos 5 Credentials Provider" + IDS_NC_PWD_BANNER "Changing Kerberos 5 Password" + IDS_NC_PWD_PWD "Current Password" + IDS_NC_PWD_NPWD "New Password" + IDS_NC_PWD_NPWD_AGAIN "New Password again" + IDS_KRB5_CREDTEXT_P0 "<p><a id=""SwitchPanel"" param=""Krb5Cred""><b>Krb5</b></a><tab>: Changing password for %s</p>" + IDS_K5CFG_IMPORT_OPTIONS + "Never\000Always\000Only when the principal name matches\000 \000" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/windows/identity/plugins/krb5/lang/krb5_msgs.mc b/src/windows/identity/plugins/krb5/lang/krb5_msgs.mc new file mode 100644 index 000000000..22f973f93 --- /dev/null +++ b/src/windows/identity/plugins/krb5/lang/krb5_msgs.mc @@ -0,0 +1,151 @@ +; // ** krb5_msgs.mc + +; /* Since .mc files can contain strings from any language, we define +; all our messages in one file in the /lang/ directory instead of +; language specific subdirectories. */ + +; /* The type is set to (wchar_t *) because that's what we will be +; feeding kherr_report() function. */ + +; // MessageIdTypedef=LPWSTR + +; /* Severity values as defined in the message definition file are +; currently ignored. */ + +SeverityNames=( + Success=0x0 +) + +LanguageNames=( + English=0x409:MSG_ENU +) + +OutputBase=16 + +; /* Actual messages start here */ + +MessageId=1 +Severity=Success +SymbolicName=MSG_INITIAL +Language=English +Initial placeholder message +. + +MessageId= +SymbolicName=MSG_CTX_INITAL_CREDS +Language=English +Obtaining initial Krb5 credentials +. + +MessageId= +SymbolicName=MSG_CTX_RENEW_CREDS +Language=English +Renewing Krb5 credentials +. + +MessageId= +SymbolicName=MSG_ERR_UNKNOWN +Language=English +An unknown error has occurred. +. + +MessageId= +SymbolicName=MSG_ERR_PR_UNKNOWN +Language=English +You have entered an unknown username/instance/realm combination. +. + +MessageId= +SymbolicName=MSG_ERR_TKFIL +Language=English +The tickets could not be accessed from the memory location where they were stored. +. + +MessageId= +SymbolicName=MSG_ERR_S_TKFIL +Language=English +This may be due to a problem with the memory where your tickets are stored. Restarting your computer might be worth a try. +. + +MessageId= +SymbolicName=MSG_ERR_CLOCKSKEW +Language=English +Your computer's clock is out of sync with the Kerberos server. +. + +MessageId= +SymbolicName=MSG_ERR_S_CLOCKSKEW +Language=English +Synchronize your clock withe the Kerberos server. +. + +MessageId= +SymbolicName=MSG_ERR_KDC_CONTACT +Language=English +Cannot contact the Kerberos server for the requested realm. +. + +MessageId= +SymbolicName=MSG_ERR_INSECURE_PW +Language=English +You have entered an insecure or weak password. +. + +MessageId= +SymbolicName=MSG_ERR_NO_IDENTITY +Language=English +There were no identities for which to renew credentials. +. + +MessageId= +SymbolicName=MSG_CTX_PASSWD +Language=English +Changing Kerberos 5 Password +. + +MessageId= +SymbolicName=MSG_PWD_UNKNOWN +Language=English +Unknown error +. + +MessageId= +SymbolicName=MSG_PWD_NOT_SAME +Language=English +The new passwords are not the same. +. + +MessageId= +SymbolicName=MSG_PWD_S_NOT_SAME +Language=English +The new password is asked for twice to protect against a mistake when setting the new password. Both instances of the new password must be the same. Please correct this and try again. +. + +MessageId= +SymbolicName=MSG_PWD_SAME +Language=English +The new and the old passwords are the same. +. + +MessageId= +SymbolicName=MSG_PWD_S_SAME +Language=English +Please type a new password to continue. +. + +MessageId= +SymbolicName=MSG_PWD_NO_IDENTITY +Language=English +There are no identities selected. +. + +MessageId= +SymbolicName=MSG_PWD_S_NO_IDENTITY +Language=English +Please select an identity to change the password. +. + +MessageId= +SymbolicName=MSG_ +Language=English +. diff --git a/src/windows/identity/plugins/krb5/langres.h b/src/windows/identity/plugins/krb5/langres.h new file mode 100644 index 000000000..87f74f547 --- /dev/null +++ b/src/windows/identity/plugins/krb5/langres.h @@ -0,0 +1,127 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by D:\work\khimaira\src\plugins\krb5\lang\en_us\langres.rc +// +#define IDS_UNK_ADDR_FMT 101 +#define IDD_NC_KRB5 102 +#define IDS_KRB5_CREDTEXT_0 102 +#define IDS_KRB5_CCNAME_SHORT_DESC 103 +#define IDS_KEY_ENCTYPE_SHORT_DESC 104 +#define IDD_CONFIG 104 +#define IDS_TKT_ENCTYPE_SHORT_DESC 105 +#define IDD_CFG_REALMS 105 +#define IDS_KEY_ENCTYPE_LONG_DESC 106 +#define IDD_CFG_IDS_TAB 106 +#define IDS_TKT_ENCTYPE_LONG_DESC 107 +#define IDD_PP_KRB5C 107 +#define IDS_ADDR_LIST_SHORT_DESC 108 +#define IDD_PP_KRB5 108 +#define IDS_ADDR_LIST_LONG_DESC 109 +#define IDD_CFG_ID_TAB 109 +#define IDS_ETYPE_NULL 110 +#define IDD_NC_KRB5_PASSWORD 110 +#define IDS_ETYPE_DES_CBC_CRC 111 +#define IDS_ETYPE_DES_CBC_MD4 112 +#define IDS_ETYPE_DES_CBC_MD5 113 +#define IDS_ETYPE_DES_CBC_RAW 114 +#define IDS_ETYPE_DES3_CBC_SHA 115 +#define IDS_ETYPE_DES3_CBC_RAW 116 +#define IDS_ETYPE_DES_HMAC_SHA1 117 +#define IDS_ETYPE_DES3_CBC_SHA1 118 +#define IDS_ETYPE_AES128_CTS_HMAC_SHA1_96 119 +#define IDS_ETYPE_AES256_CTS_HMAC_SHA1_96 120 +#define IDS_ETYPE_ARCFOUR_HMAC 121 +#define IDS_ETYPE_ARCFOUR_HMAC_EXP 122 +#define IDS_ETYPE_UNKNOWN 123 +#define IDS_ETYPE_LOCAL_DES3_HMAC_SHA1 124 +#define IDS_ETYPE_LOCAL_RC4_MD4 125 +#define IDS_KRB5_SHORT_DESC 126 +#define IDS_KRB5_LONG_DESC 127 +#define IDS_KRB4_SHORT_DESC 128 +#define IDS_KRB4_LONG_DESC 129 +#define IDS_KRB5_FLAGS_SHORT_DESC 130 +#define IDS_RENEW_TILL_SHORT_DESC 131 +#define IDS_RENEW_TILL_LONG_DESC 132 +#define IDS_RENEW_FOR_SHORT_DESC 133 +#define IDS_RENEW_FOR_LONG_DESC 134 +#define IDS_KRB5_CCNAME_LONG_DESC 135 +#define IDS_NC_USERNAME 136 +#define IDS_NC_REALM 137 +#define IDS_KRB5_WARNING 138 +#define IDS_K5ERR_NAME_EXPIRED 139 +#define IDS_K5ERR_KEY_EXPIRED 140 +#define IDS_KRB5_WARN_FMT 141 +#define IDS_K5ERR_FMT 142 +#define IDS_K5CFG_SHORT_DESC 143 +#define IDS_K5CFG_LONG_DESC 144 +#define IDS_K5RLM_SHORT_DESC 145 +#define IDS_K5RLM_LONG_DESC 146 +#define IDS_K5CFG_IDS_SHORT_DESC 147 +#define IDS_K5CFG_IDS_LONG_DESC 148 +#define IDS_K5CFG_ID_SHORT_DESC 149 +#define IDS_K5CFG_ID_LONG_DESC 150 +#define IDS_PLUGIN_DESC 151 +#define IDS_NC_PWD_BANNER 152 +#define IDS_NC_PWD_PWD 153 +#define IDS_NC_PWD_NPWD 154 +#define IDS_NC_PWD_NPWD_AGAIN 155 +#define IDS_KRB5_CREDTEXT_P0 156 +#define IDS_K5CFG_IMPORT_OPTIONS 157 +#define IDC_NCK5_RENEWABLE 1002 +#define IDC_NCK5_FORWARDABLE 1004 +#define IDC_NCK5_REALM 1005 +#define IDC_NCK5_ADD_REALMS 1006 +#define IDC_NCK5_LIFETIME_EDIT 1008 +#define IDC_NCK5_RENEW_EDIT 1009 +#define IDC_PPK5_CRENEW 1014 +#define IDC_PPK5_CFORWARD 1015 +#define IDC_PPK5_CPROXY 1016 +#define IDC_PPK5_NAME 1017 +#define IDC_PPK5_ISSUE 1018 +#define IDC_PPK5_VALID 1019 +#define IDC_PPK5_RENEW 1020 +#define IDC_CHECK2 1022 +#define IDC_CHECK4 1024 +#define IDC_PPK5_LIFETIME 1024 +#define IDC_CHECK5 1025 +#define IDC_CFG_LBL_REALM 1025 +#define IDC_CFG_DEFREALM 1026 +#define IDC_CFG_LBL_CFGFILE 1029 +#define IDC_CFG_CFGFILE 1030 +#define IDC_CFG_WINGRP 1031 +#define IDC_LBL_IMPORT 1032 +#define IDC_CFG_IMPORT 1033 +#define IDC_CFG_LBL_HOSTNAME 1034 +#define IDC_CFG_HOSTNAME 1035 +#define IDC_CFG_LBL_DOMAIN 1036 +#define IDC_CFG_DOMAIN 1037 +#define IDC_CFG_CREATECONFIG 1038 +#define IDC_CFG_BROWSE 1039 +#define IDC_CFG_CFGFILEGRP 1040 +#define IDC_CFG_CFGREALMS 1041 +#define IDC_CFG_REALMS 1044 +#define IDC_CFG_DOMAINGRP 1045 +#define IDC_CFG_SERVERSGRP 1046 +#define IDC_LIST3 1047 +#define IDC_LIST4 1048 +#define IDC_CFG_LBL_DEFLIFE 1049 +#define IDC_CFG_DEFLIFE 1050 +#define IDC_CFG_LBL_DEFRLIFE 1051 +#define IDC_CFG_DEFRLIFE 1052 +#define IDC_CFG_LIFEGRP 1053 +#define IDC_CFG_LRNG_MIN 1054 +#define IDC_CFG_LRNG_MAX 1055 +#define IDC_CFG_RLRNG_MIN 1056 +#define IDC_CFG_RLRNG_MAX 1057 +#define IDC_CFG_CCACHE 1058 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 111 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1059 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/windows/identity/plugins/krb5/main.c b/src/windows/identity/plugins/krb5/main.c new file mode 100644 index 000000000..db996d951 --- /dev/null +++ b/src/windows/identity/plugins/krb5/main.c @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<krbcred.h> +#include<kherror.h> + +kmm_module h_khModule; /* KMM's handle to this module */ +HINSTANCE hInstance; +HMODULE hResModule; /* HMODULE to the resource library */ +const wchar_t * k5_facility = L"Krb5"; + +khm_int32 type_id_enctype = -1; +khm_int32 type_id_addr_list = -1; +khm_int32 type_id_krb5_flags = -1; + +BOOL type_regd_enctype = FALSE; +BOOL type_regd_addr_list = FALSE; +BOOL type_regd_krb5_flags = FALSE; + +khm_int32 attr_id_key_enctype = -1; +khm_int32 attr_id_tkt_enctype = -1; +khm_int32 attr_id_addr_list = -1; +khm_int32 attr_id_krb5_flags = -1; +khm_int32 attr_id_krb5_ccname = -1; + +BOOL attr_regd_key_enctype = FALSE; +BOOL attr_regd_tkt_enctype = FALSE; +BOOL attr_regd_addr_list = FALSE; +BOOL attr_regd_krb5_flags = FALSE; +BOOL attr_regd_krb5_ccname = FALSE; + +khm_handle csp_plugins = NULL; +khm_handle csp_krbcred = NULL; +khm_handle csp_params = NULL; + +BOOL is_k5_identpro = TRUE; + +kmm_module_locale locales[] = { + LOCALE_DEF(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US), L"krb5cred_en_us.dll", KMM_MLOC_FLAG_DEFAULT) +}; +int n_locales = ARRAYLENGTH(locales); + +/* These two should not do anything */ +void init_krb() { +} + +void exit_krb() { +} + +/* called by the NetIDMgr module manager */ +KHMEXP khm_int32 KHMAPI init_module(kmm_module h_module) { + khm_int32 rv = KHM_ERROR_SUCCESS; + kmm_plugin_reg pi; + wchar_t buf[256]; + + h_khModule = h_module; + + rv = kmm_set_locale_info(h_module, locales, n_locales); + if(KHM_SUCCEEDED(rv)) { + hResModule = kmm_get_resource_hmodule(h_module); + } else + goto _exit; + + /* register the plugin */ + ZeroMemory(&pi, sizeof(pi)); + pi.name = KRB5_PLUGIN_NAME; + pi.type = KHM_PITYPE_CRED; + pi.icon = NULL; /*TODO: Assign icon */ + pi.flags = KHM_PIFLAG_IDENTITY_PROVIDER; + pi.msg_proc = k5_msg_callback; + pi.description = buf; + LoadString(hResModule, IDS_PLUGIN_DESC, + buf, ARRAYLENGTH(buf)); + kmm_provide_plugin(h_module, &pi); + + if(KHM_FAILED(rv = init_imports())) + goto _exit; + + if(KHM_FAILED(rv = init_error_funcs())) + goto _exit; + + /* Register common data types */ + if(KHM_FAILED(kcdb_type_get_id(TYPENAME_ENCTYPE, &type_id_enctype))) { + kcdb_type type; + kcdb_type *t32; + + kcdb_type_get_info(KCDB_TYPE_INT32, &t32); + + type.id = KCDB_TYPE_INVALID; + type.name = TYPENAME_ENCTYPE; + type.flags = KCDB_TYPE_FLAG_CB_FIXED; + type.cb_max = t32->cb_max; + type.cb_min = t32->cb_min; + type.isValid = t32->isValid; + type.comp = t32->comp; + type.dup = t32->dup; + type.toString = enctype_toString; + + rv = kcdb_type_register(&type, &type_id_enctype); + kcdb_type_release_info(t32); + + if(KHM_FAILED(rv)) + goto _exit; + type_regd_enctype = TRUE; + } + + if(KHM_FAILED(kcdb_type_get_id(TYPENAME_ADDR_LIST, &type_id_addr_list))) { + kcdb_type type; + kcdb_type *tdata; + + kcdb_type_get_info(KCDB_TYPE_DATA, &tdata); + + type.id = KCDB_TYPE_INVALID; + type.name = TYPENAME_ADDR_LIST; + type.flags = KCDB_TYPE_FLAG_CB_MIN; + type.cb_min = 0; + type.cb_max = 0; + type.isValid = tdata->isValid; + type.comp = tdata->comp; + type.dup = tdata->dup; + type.toString = addr_list_toString; + + rv = kcdb_type_register(&type, &type_id_addr_list); + kcdb_type_release_info(tdata); + + if(KHM_FAILED(rv)) + goto _exit; + type_regd_addr_list = TRUE; + } + + if(KHM_FAILED(kcdb_type_get_id(TYPENAME_KRB5_FLAGS, &type_id_krb5_flags))) { + kcdb_type type; + kcdb_type *t32; + + kcdb_type_get_info(KCDB_TYPE_INT32, &t32); + + type.id = KCDB_TYPE_INVALID; + type.name = TYPENAME_KRB5_FLAGS; + type.flags = KCDB_TYPE_FLAG_CB_FIXED; + type.cb_max = t32->cb_max; + type.cb_min = t32->cb_min; + type.isValid = t32->isValid; + type.comp = t32->comp; + type.dup = t32->dup; + type.toString = krb5flags_toString; + + rv = kcdb_type_register(&type, &type_id_krb5_flags); + kcdb_type_release_info(t32); + + if(KHM_FAILED(rv)) + goto _exit; + type_regd_krb5_flags = TRUE; + } + + /* Register common attributes */ + if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_KEY_ENCTYPE, &attr_id_key_enctype))) { + kcdb_attrib attrib; + wchar_t sbuf[KCDB_MAXCCH_SHORT_DESC]; + wchar_t lbuf[KCDB_MAXCCH_SHORT_DESC]; + /* although we are loading a long descriptoin, it still fits + in the short descriptoin buffer */ + + ZeroMemory(&attrib, sizeof(attrib)); + + attrib.name = ATTRNAME_KEY_ENCTYPE; + attrib.id = KCDB_ATTR_INVALID; + attrib.type = type_id_enctype; + attrib.flags = 0; + LoadString(hResModule, IDS_KEY_ENCTYPE_SHORT_DESC, sbuf, ARRAYLENGTH(sbuf)); + LoadString(hResModule, IDS_KEY_ENCTYPE_LONG_DESC, lbuf, ARRAYLENGTH(lbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = lbuf; + + rv = kcdb_attrib_register(&attrib, &attr_id_key_enctype); + + if(KHM_FAILED(rv)) + goto _exit; + + attr_regd_key_enctype = TRUE; + } + + if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_TKT_ENCTYPE, &attr_id_tkt_enctype))) { + kcdb_attrib attrib; + wchar_t sbuf[KCDB_MAXCCH_SHORT_DESC]; + wchar_t lbuf[KCDB_MAXCCH_SHORT_DESC]; + /* although we are loading a long descriptoin, it still fits + in the short descriptoin buffer */ + + ZeroMemory(&attrib, sizeof(attrib)); + + attrib.name = ATTRNAME_TKT_ENCTYPE; + attrib.id = KCDB_ATTR_INVALID; + attrib.type = type_id_enctype; + attrib.flags = 0; + LoadString(hResModule, IDS_TKT_ENCTYPE_SHORT_DESC, sbuf, ARRAYLENGTH(sbuf)); + LoadString(hResModule, IDS_TKT_ENCTYPE_LONG_DESC, lbuf, ARRAYLENGTH(lbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = lbuf; + + rv = kcdb_attrib_register(&attrib, &attr_id_tkt_enctype); + + if(KHM_FAILED(rv)) + goto _exit; + + attr_regd_tkt_enctype = TRUE; + } + + if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_ADDR_LIST, &attr_id_addr_list))) { + kcdb_attrib attrib; + wchar_t sbuf[KCDB_MAXCCH_SHORT_DESC]; + wchar_t lbuf[KCDB_MAXCCH_SHORT_DESC]; + /* although we are loading a long descriptoin, it still fits + in the short descriptoin buffer */ + + ZeroMemory(&attrib, sizeof(attrib)); + + attrib.name = ATTRNAME_ADDR_LIST; + attrib.id = KCDB_ATTR_INVALID; + attrib.type = type_id_addr_list; + attrib.flags = 0; + LoadString(hResModule, IDS_ADDR_LIST_SHORT_DESC, sbuf, ARRAYLENGTH(sbuf)); + LoadString(hResModule, IDS_ADDR_LIST_LONG_DESC, lbuf, ARRAYLENGTH(lbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = lbuf; + + rv = kcdb_attrib_register(&attrib, &attr_id_addr_list); + + if(KHM_FAILED(rv)) + goto _exit; + + attr_regd_addr_list = TRUE; + } + + if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_KRB5_FLAGS, &attr_id_krb5_flags))) { + kcdb_attrib attrib; + wchar_t sbuf[KCDB_MAXCCH_SHORT_DESC]; + + /* although we are loading a long descriptoin, it still fits + in the short descriptoin buffer */ + + ZeroMemory(&attrib, sizeof(attrib)); + + attrib.name = ATTRNAME_KRB5_FLAGS; + attrib.id = KCDB_ATTR_INVALID; + attrib.type = type_id_krb5_flags; + attrib.flags = 0; + LoadString(hResModule, IDS_KRB5_FLAGS_SHORT_DESC, sbuf, ARRAYLENGTH(sbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = NULL; + + rv = kcdb_attrib_register(&attrib, &attr_id_krb5_flags); + + if(KHM_FAILED(rv)) + goto _exit; + + attr_regd_krb5_flags = TRUE; + } + + if(KHM_FAILED(kcdb_attrib_get_id(ATTRNAME_KRB5_CCNAME, &attr_id_krb5_ccname))) { + kcdb_attrib attrib; + wchar_t sbuf[KCDB_MAXCCH_SHORT_DESC]; + wchar_t lbuf[KCDB_MAXCCH_SHORT_DESC]; + /* although we are loading a long descriptoin, it still fits + in the short descriptoin buffer */ + + ZeroMemory(&attrib, sizeof(attrib)); + + attrib.name = ATTRNAME_KRB5_CCNAME; + attrib.id = KCDB_ATTR_INVALID; + attrib.type = KCDB_TYPE_STRING; + attrib.flags = KCDB_ATTR_FLAG_PROPERTY; + LoadString(hResModule, IDS_KRB5_CCNAME_SHORT_DESC, sbuf, ARRAYLENGTH(sbuf)); + LoadString(hResModule, IDS_KRB5_CCNAME_LONG_DESC, lbuf, ARRAYLENGTH(lbuf)); + attrib.short_desc = sbuf; + attrib.long_desc = lbuf; + + rv = kcdb_attrib_register(&attrib, &attr_id_krb5_ccname); + + if(KHM_FAILED(rv)) + goto _exit; + + attr_regd_krb5_ccname = TRUE; + } + + rv = kmm_get_plugins_config(0, &csp_plugins); + if(KHM_FAILED(rv)) goto _exit; + + rv = khc_load_schema(csp_plugins, schema_krbconfig); + if(KHM_FAILED(rv)) goto _exit; + + rv = khc_open_space(csp_plugins, CSNAME_KRB5CRED, 0, &csp_krbcred); + if(KHM_FAILED(rv)) goto _exit; + + rv = khc_open_space(csp_krbcred, CSNAME_PARAMS, 0, &csp_params); + if(KHM_FAILED(rv)) goto _exit; + +_exit: + return rv; +} + +/* called by the NetIDMgr module manager */ +KHMEXP khm_int32 KHMAPI exit_module(kmm_module h_module) { + exit_imports(); + exit_error_funcs(); + + if(attr_regd_key_enctype) + kcdb_attrib_unregister(attr_id_key_enctype); + if(attr_regd_tkt_enctype) + kcdb_attrib_unregister(attr_id_tkt_enctype); + if(attr_regd_addr_list) + kcdb_attrib_unregister(attr_id_addr_list); + if(attr_regd_krb5_flags) + kcdb_attrib_unregister(attr_id_krb5_flags); + if(attr_regd_krb5_ccname) + kcdb_attrib_unregister(attr_id_krb5_ccname); + + if(type_regd_enctype) + kcdb_type_unregister(type_id_enctype); + if(type_regd_addr_list) + kcdb_type_unregister(type_id_addr_list); + if(type_regd_krb5_flags) + kcdb_type_unregister(type_id_krb5_flags); + + if(csp_params) { + khc_close_space(csp_params); + csp_params = NULL; + } + + if(csp_krbcred) { + khc_close_space(csp_krbcred); + csp_krbcred = NULL; + } + + if(csp_plugins) { + khc_unload_schema(csp_plugins, schema_krbconfig); + khc_close_space(csp_plugins); + csp_plugins = NULL; + } + + return KHM_ERROR_SUCCESS; /* the return code is ignored */ +} + +BOOL WINAPI DllMain( + HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved +) +{ + switch(fdwReason) { + case DLL_PROCESS_ATTACH: + hInstance = hinstDLL; + init_krb(); + break; + case DLL_PROCESS_DETACH: + exit_krb(); + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + } + + return TRUE; +} diff --git a/src/windows/identity/ui/Makefile b/src/windows/identity/ui/Makefile new file mode 100644 index 000000000..402fc5d88 --- /dev/null +++ b/src/windows/identity/ui/Makefile @@ -0,0 +1,81 @@ +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +MODULE=ui +!include <../config/Makefile.w32> + +EXEFILE=$(BINDIR)\netidmgr.exe + +MANIFESTFILE=$(BINDIR)\netidmgr.exe.manifest + +OBJFILES= \ + $(OBJ)\main.obj \ + $(OBJ)\mainmenu.obj \ + $(OBJ)\toolbar.obj \ + $(OBJ)\statusbar.obj \ + $(OBJ)\notifier.obj \ + $(OBJ)\timer.obj \ + $(OBJ)\uiconfig.obj \ + $(OBJ)\mainwnd.obj \ + $(OBJ)\credwnd.obj \ + $(OBJ)\htwnd.obj \ + $(OBJ)\passwnd.obj \ + $(OBJ)\newcredwnd.obj \ + $(OBJ)\propertywnd.obj \ + $(OBJ)\credfuncs.obj \ + $(OBJ)\configwnd.obj \ + $(OBJ)\aboutwnd.obj \ + $(OBJ)\reqdaemon.obj \ + $(OBJ)\cfg_general_wnd.obj \ + $(OBJ)\cfg_identities_wnd.obj \ + $(OBJ)\cfg_notif_wnd.obj \ + $(OBJ)\cfg_plugins_wnd.obj + +RESFILE=$(OBJ)\khapp.res + +LIBFILES= \ + $(LIBDIR)\nidmgr32.lib + +SDKLIBFILES= \ + comctl32.lib \ + shell32.lib + +$(OBJ)\uiconfig.c: uiconfig.csv $(CONFDIR)\csvschema.cfg + $(CCSV) $** $@ + +$(RESFILE): lang\en_us\khapp.rc + $(RC2RES) + +!if "$(KH_BUILD)"=="RETAIL" +$(MANIFESTFILE): netidmgr.manifest.$(CPU).$(KH_CLVER) +!else +$(MANIFESTFILE): netidmgr.manifest.$(CPU).$(KH_CLVER).debug +!endif + $(CP) $** $@ + +$(EXEFILE): $(OBJFILES) $(RESFILE) $(LIBFILES) + $(EXEGUILINK) $(SDKLIBFILES) + +all: mkdirs $(EXEFILE) $(MANIFESTFILE) + diff --git a/src/windows/identity/ui/aboutwnd.c b/src/windows/identity/ui/aboutwnd.c new file mode 100644 index 000000000..2dde601a4 --- /dev/null +++ b/src/windows/identity/ui/aboutwnd.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khmapp.h> +#include<khimaira_version.h> +#include<tlhelp32.h> + +#if DEBUG +#include<assert.h> +#endif + +INT_PTR CALLBACK +about_dlg_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + + switch(uMsg) { + case WM_INITDIALOG: + { + HANDLE hsnap; + HWND hw; + + SetDlgItemText(hwnd, IDC_PRODUCT, + TEXT(KH_VERSTR_PRODUCT_1033)); + SetDlgItemText(hwnd, IDC_COPYRIGHT, + TEXT(KH_VERSTR_COPYRIGHT_1033)); + SetDlgItemText(hwnd, IDC_BUILDINFO, + TEXT(KH_VERSTR_BUILDINFO_1033)); + + hsnap = + CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, + 0); + + if (hsnap != INVALID_HANDLE_VALUE) { + LVCOLUMN lvc; + MODULEENTRY32 mod; + RECT r; + + hw = GetDlgItem(hwnd, IDC_MODULES); +#ifdef DEBUG + assert(hw != NULL); +#endif + + GetWindowRect(hw, &r); + OffsetRect(&r, -r.left, -r.top); + + ZeroMemory(&lvc, sizeof(lvc)); + lvc.mask = LVCF_TEXT | LVCF_WIDTH; + + lvc.pszText = L"Name"; + lvc.cx = r.right / 4; + + ListView_InsertColumn(hw, 0, &lvc); + + lvc.pszText = L"Path"; + lvc.cx = (r.right * 3) / 4; + ListView_InsertColumn(hw, 1, &lvc); + + ZeroMemory(&mod, sizeof(mod)); + mod.dwSize = sizeof(mod); + + /* done with columns, now for the actual data */ + if (!Module32First(hsnap, &mod)) + goto _done_with_modules; + + do { + + LVITEM lvi; + int idx; + + ZeroMemory(&lvi, sizeof(lvi)); + + lvi.mask = LVIF_TEXT; + lvi.pszText = mod.szModule; + idx = ListView_InsertItem(hw, &lvi); + + lvi.mask = LVIF_TEXT; + lvi.iItem = idx; + lvi.iSubItem = 1; + lvi.pszText = mod.szExePath; + ListView_SetItem(hw, &lvi); + + ZeroMemory(&mod, sizeof(mod)); + mod.dwSize = sizeof(mod); + } while(Module32Next(hsnap, &mod)); + + ListView_SetView(hw, LV_VIEW_DETAILS); + + _done_with_modules: + CloseHandle(hsnap); + } + + khm_add_dialog(hwnd); + khm_enter_modal(hwnd); + } + return FALSE; + + case WM_DESTROY: + khm_leave_modal(); + khm_del_dialog(hwnd); + return TRUE; + + case WM_COMMAND: + if (wParam == MAKEWPARAM(IDOK, BN_CLICKED)) + DestroyWindow(hwnd); + return TRUE; + } + + return FALSE; +} + +void +khm_create_about_window(void) { + HWND hwnd; + hwnd = CreateDialog(khm_hInstance, + MAKEINTRESOURCE(IDD_ABOUT), + khm_hwnd_main, + about_dlg_proc); + + ShowWindow(hwnd, SW_SHOW); + /* no need to keep track of the hwnd, since we add it to the + dialog chain in the dialog procedure */ +} diff --git a/src/windows/identity/ui/aboutwnd.h b/src/windows/identity/ui/aboutwnd.h new file mode 100644 index 000000000..a427b7dd0 --- /dev/null +++ b/src/windows/identity/ui/aboutwnd.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_ABOUTWND_H +#define __KHIMAIRA_ABOUTWND_H + +void +khm_create_about_window(void); + +#endif diff --git a/src/windows/identity/ui/appglobal.h b/src/windows/identity/ui/appglobal.h new file mode 100644 index 000000000..41fca2d17 --- /dev/null +++ b/src/windows/identity/ui/appglobal.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_APPGLOBAL_H +#define __KHIMAIRA_APPGLOBAL_H + +/* global data */ +extern HINSTANCE khm_hInstance; +extern int khm_nCmdShow; +extern const wchar_t * khm_facility; +extern kconf_schema schema_uiconfig[]; + +typedef struct tag_khm_startup_options { + BOOL seen; + BOOL processing; + + BOOL init; + BOOL import; + BOOL renew; + BOOL destroy; + + BOOL autoinit; + BOOL exit; + BOOL error_exit; + + BOOL no_main_window; +} khm_startup_options; + +extern khm_startup_options khm_startup; + +void khm_add_dialog(HWND dlg); +void khm_del_dialog(HWND dlg); +BOOL khm_is_dialog_active(void); + +void khm_enter_modal(HWND hwnd); +void khm_leave_modal(void); + +void khm_add_property_sheet(khui_property_sheet * s); +void khm_del_property_sheet(khui_property_sheet * s); + +void khm_init_gui(void); +void khm_exit_gui(void); + +void khm_parse_commandline(); +void khm_register_window_classes(void); + +#define MAX_RES_STRING 1024 + +#define ELIPSIS L"..." + +#endif diff --git a/src/windows/identity/ui/cfg_general_wnd.c b/src/windows/identity/ui/cfg_general_wnd.c new file mode 100644 index 000000000..37c7ba7d1 --- /dev/null +++ b/src/windows/identity/ui/cfg_general_wnd.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khmapp.h> +#include<assert.h> + +typedef struct tag_cfg_data { + BOOL auto_init; + BOOL auto_start; + BOOL auto_import; + BOOL keep_running; + BOOL auto_detect_net; +} cfg_data; + +typedef struct tag_dlg_data { + khui_config_node node; + cfg_data saved; + cfg_data work; +} dlg_data; + +static void +read_params(dlg_data * dd) { + cfg_data * d; + khm_handle csp_cw; + khm_int32 t; + + d = &dd->saved; + + if (KHM_FAILED(khc_open_space(NULL, L"CredWindow", KHM_PERM_READ, + &csp_cw))) { +#ifdef DEBUG + assert(FALSE); +#endif + return; + } + + khc_read_int32(csp_cw, L"AutoInit", &t); + d->auto_init = !!t; + + khc_read_int32(csp_cw, L"AutoStart", &t); + d->auto_start = !!t; + + khc_read_int32(csp_cw, L"AutoImport", &t); + d->auto_import = !!t; + + khc_read_int32(csp_cw, L"KeepRunning", &t); + d->keep_running = !!t; + + khc_read_int32(csp_cw, L"AutoDetectNet", &t); + d->auto_detect_net = !!t; + + khc_close_space(csp_cw); + + dd->work = *d; +} + +static void +write_params(dlg_data * dd) { + cfg_data * d, * s; + khm_handle csp_cw; + + d = &dd->work; + s = &dd->saved; + + if (KHM_FAILED(khc_open_space(NULL, L"CredWindow", KHM_PERM_WRITE, + &csp_cw))) { +#ifdef DEBUG + assert(FALSE); +#endif + return; + } + + if (!!d->auto_init != !!s->auto_init) + khc_write_int32(csp_cw, L"AutoInit", d->auto_init); + + if (!!d->auto_start != !!s->auto_start) + khc_write_int32(csp_cw, L"AutoStart", d->auto_start); + + if (!!d->auto_import != !!s->auto_import) + khc_write_int32(csp_cw, L"AutoImport", d->auto_import); + + if (!!d->keep_running != !!s->keep_running) + khc_write_int32(csp_cw, L"KeepRunning", d->keep_running); + + if (!!d->auto_detect_net != !!s->auto_detect_net) + khc_write_int32(csp_cw, L"AutoDetectNet", d->auto_detect_net); + + khc_close_space(csp_cw); + + khui_cfg_set_flags(dd->node, + KHUI_CNFLAG_APPLIED, + KHUI_CNFLAG_APPLIED | KHUI_CNFLAG_MODIFIED); + + *s = *d; +} + +static void +check_for_modification(dlg_data * dd) { + cfg_data * d, * s; + d = &dd->work; + s = &dd->saved; + + if (!!d->auto_init != !!s->auto_init || + !!d->auto_start != !!s->auto_start || + !!d->auto_import != !!s->auto_import || + !!d->keep_running != !!s->keep_running || + !!d->auto_detect_net != !!s->auto_detect_net) { + + khui_cfg_set_flags(dd->node, + KHUI_CNFLAG_MODIFIED, + KHUI_CNFLAG_MODIFIED); + + } else { + + khui_cfg_set_flags(dd->node, + 0, + KHUI_CNFLAG_MODIFIED); + + } +} + +static void +refresh_view(HWND hwnd, dlg_data * d) { + CheckDlgButton(hwnd, IDC_CFG_AUTOINIT, + (d->work.auto_init?BST_CHECKED:BST_UNCHECKED)); + CheckDlgButton(hwnd, IDC_CFG_AUTOSTART, + (d->work.auto_start?BST_CHECKED:BST_UNCHECKED)); + CheckDlgButton(hwnd, IDC_CFG_AUTOIMPORT, + (d->work.auto_import?BST_CHECKED:BST_UNCHECKED)); + CheckDlgButton(hwnd, IDC_CFG_KEEPRUNNING, + (d->work.keep_running?BST_CHECKED:BST_UNCHECKED)); + CheckDlgButton(hwnd, IDC_CFG_NETDETECT, + (d->work.auto_detect_net?BST_CHECKED:BST_UNCHECKED)); +} + +static void +refresh_data(HWND hwnd, dlg_data * d) { + d->work.auto_init = (IsDlgButtonChecked(hwnd, IDC_CFG_AUTOINIT) + == BST_CHECKED); + d->work.auto_start = (IsDlgButtonChecked(hwnd, IDC_CFG_AUTOSTART) + == BST_CHECKED); + d->work.auto_import = (IsDlgButtonChecked(hwnd, IDC_CFG_AUTOIMPORT) + == BST_CHECKED); + d->work.keep_running = (IsDlgButtonChecked(hwnd, IDC_CFG_KEEPRUNNING) + == BST_CHECKED); + d->work.auto_detect_net = (IsDlgButtonChecked(hwnd, IDC_CFG_NETDETECT) + == BST_CHECKED); +} + +INT_PTR CALLBACK +khm_cfg_general_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + dlg_data * d; + + switch(uMsg) { + case WM_INITDIALOG: + d = malloc(sizeof(*d)); +#ifdef DEBUG + assert(d != NULL); +#endif + +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d); +#pragma warning(pop) + + ZeroMemory(d, sizeof(*d)); + + d->node = (khui_config_node) lParam; + + read_params(d); + + refresh_view(hwnd, d); + + return FALSE; + + case WM_COMMAND: + d = (dlg_data *) (DWORD_PTR) GetWindowLongPtr(hwnd, DWLP_USER); + + if (HIWORD(wParam) == BN_CLICKED) { + refresh_data(hwnd, d); + check_for_modification(d); + } + + khm_set_dialog_result(hwnd, 0); + + return TRUE; + + case KHUI_WM_CFG_NOTIFY: + d = (dlg_data *) (DWORD_PTR) GetWindowLongPtr(hwnd, DWLP_USER); + + if (HIWORD(wParam) == WMCFG_APPLY) { + write_params(d); + } + + khm_set_dialog_result(hwnd, 0); + + return TRUE; + } + + return FALSE; +} diff --git a/src/windows/identity/ui/cfg_identities_wnd.c b/src/windows/identity/ui/cfg_identities_wnd.c new file mode 100644 index 000000000..1cef2d7ce --- /dev/null +++ b/src/windows/identity/ui/cfg_identities_wnd.c @@ -0,0 +1,1256 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khmapp.h> +#include<assert.h> + +static khui_config_node +get_window_node(HWND hwnd) { + return (khui_config_node) (LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); +} + +static void +set_window_node(HWND hwnd, khui_config_node node) { +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, DWLP_USER, + (LONG_PTR) node); +#pragma warning(pop) +} + +static void +add_subpanels(HWND hwnd, + khui_config_node ctx_node, + khui_config_node ref_node) { + HWND hw_tab; + HWND hw_target; + khui_config_node sub; + khui_config_node_reg reg; + khui_config_init_data idata; + int idx; + + hw_tab = GetDlgItem(hwnd, IDC_CFG_TAB); + hw_target = GetDlgItem(hwnd, IDC_CFG_TARGET); +#ifdef DEBUG + assert(hw_tab); + assert(hw_target); +#endif + + if (KHM_FAILED(khui_cfg_get_first_subpanel(ref_node, &sub))) { +#ifdef DEBUG + assert(FALSE); +#endif + return; + } + + idx = 0; + while(sub) { + HWND hwnd_panel; + TCITEM tci; + int iid; + + khui_cfg_get_reg(sub, ®); + + if ((ctx_node == ref_node && (reg.flags & KHUI_CNFLAG_PLURAL)) || + (ctx_node != ref_node && !(reg.flags & KHUI_CNFLAG_PLURAL))) + goto _next_node; + + idata.ctx_node = ctx_node; + idata.this_node = sub; + idata.ref_node = ref_node; + + hwnd_panel = CreateDialogParam(reg.h_module, + reg.dlg_template, + hwnd, + reg.dlg_proc, + (LPARAM) &idata); + +#ifdef DEBUG + assert(hwnd_panel); +#endif + + ShowWindow(hwnd_panel, SW_HIDE); + + ZeroMemory(&tci, sizeof(tci)); + + tci.mask = TCIF_PARAM | TCIF_TEXT; + tci.lParam = (LPARAM) sub; + tci.pszText = (LPWSTR) reg.short_desc; + + iid = TabCtrl_InsertItem(hw_tab, 0, &tci); + idx++; + + if (reg.flags & KHUI_CNFLAG_PLURAL) { + khui_cfg_set_param_inst(sub, ctx_node, iid); + khui_cfg_set_hwnd_inst(sub, ctx_node, hwnd_panel); + } else { + khui_cfg_set_param(sub, iid); + khui_cfg_set_hwnd(sub, hwnd_panel); + } + + _next_node: + + khui_cfg_get_next_release(&sub); + } + + TabCtrl_SetCurSel(hw_tab, 0); +} + +static void +apply_all(HWND hwnd, + HWND hw_tab, + khui_config_node noderef) { + TCITEM tci; + HWND hw; + khui_config_node_reg reg; + int idx; + int count; + + count = TabCtrl_GetItemCount(hw_tab); + + for (idx = 0; idx < count; idx++) { + + ZeroMemory(&tci, sizeof(tci)); + + tci.mask = TCIF_PARAM; + TabCtrl_GetItem(hw_tab, + idx, + &tci); + +#ifdef DEBUG + assert(tci.lParam); +#endif + khui_cfg_get_reg((khui_config_node) tci.lParam, ®); + if (reg.flags & KHUI_CNFLAG_PLURAL) + hw = khui_cfg_get_hwnd_inst((khui_config_node) tci.lParam, + noderef); + else + hw = khui_cfg_get_hwnd((khui_config_node) tci.lParam); +#ifdef DEBUG + assert(hw); +#endif + + PostMessage(hw, KHUI_WM_CFG_NOTIFY, + MAKEWPARAM(0, WMCFG_APPLY), 0); + } +} + +static void +show_tab_panel(HWND hwnd, + khui_config_node node, + HWND hw_tab, + int idx, + BOOL show) { + TCITEM tci; + HWND hw; + HWND hw_target; + RECT r; + RECT rref; + khui_config_node_reg reg; + + ZeroMemory(&tci, sizeof(tci)); + + tci.mask = TCIF_PARAM; + TabCtrl_GetItem(hw_tab, + idx, + &tci); + +#ifdef DEBUG + assert(tci.lParam); +#endif + khui_cfg_get_reg((khui_config_node) tci.lParam, ®); + if (reg.flags & KHUI_CNFLAG_PLURAL) + hw = khui_cfg_get_hwnd_inst((khui_config_node) tci.lParam, + node); + else + hw = khui_cfg_get_hwnd((khui_config_node) tci.lParam); +#ifdef DEBUG + assert(hw); +#endif + + if (!show) { + ShowWindow(hw, SW_HIDE); + return; + } + + hw_target = GetDlgItem(hwnd, IDC_CFG_TARGET); +#ifdef DEBUG + assert(hw_target); +#endif + GetWindowRect(hwnd, &rref); + GetWindowRect(hw_target, &r); + + OffsetRect(&r, -rref.left, -rref.top); + + SetWindowPos(hw, + hw_tab, + r.left, r.top, + r.right - r.left, r.bottom - r.top, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_SHOWWINDOW); +} + +static INT_PTR +handle_cfg_notify(HWND hwnd, + WPARAM wParam, + LPARAM lParam) { + khui_config_node node; + HWND hw; + + node = get_window_node(hwnd); + + if (HIWORD(wParam) == WMCFG_APPLY) { + + hw = GetDlgItem(hwnd, IDC_CFG_TAB); + + apply_all(hwnd, + hw, + node); + } + + return TRUE; +} + +static INT_PTR +handle_notify(HWND hwnd, + WPARAM wParam, + LPARAM lParam) { + LPNMHDR lpnm; + int i; + + + khui_config_node node; + + lpnm = (LPNMHDR) lParam; + node = get_window_node(hwnd); + + if (lpnm->idFrom == IDC_CFG_TAB) { + switch(lpnm->code) { + case TCN_SELCHANGING: + i = TabCtrl_GetCurSel(lpnm->hwndFrom); + + show_tab_panel(hwnd, + node, + lpnm->hwndFrom, + i, + FALSE); + break; + + case TCN_SELCHANGE: + i = TabCtrl_GetCurSel(lpnm->hwndFrom); + + show_tab_panel(hwnd, + node, + lpnm->hwndFrom, + i, + TRUE); + break; + } + return TRUE; + } else { + return FALSE; + } +} + +typedef struct tag_ident_props { + BOOL monitor; + BOOL auto_renew; + BOOL sticky; +} ident_props; + +typedef struct tag_ident_data { + khm_handle ident; + wchar_t * idname; + int lv_idx; + + BOOL removed; + BOOL applied; + + khm_int32 flags; + + ident_props saved; + ident_props work; + + HWND hwnd; +} ident_data; + +typedef struct tag_idents_data { + BOOL valid; + + ident_data * idents; + khm_size n_idents; + khm_size nc_idents; + + int refcount; + + HIMAGELIST hi_status; + int idx_id; + int idx_default; + int idx_modified; + int idx_applied; + int idx_deleted; + + HWND hwnd; +} idents_data; + +static idents_data cfg_idents = {FALSE, NULL, 0, 0, 0, NULL }; + +static void +read_params_ident(ident_data * d) { + khm_handle csp_ident; + khm_handle csp_cw; + khm_int32 t; + + if (KHM_FAILED(kcdb_identity_get_config(d->ident, + KHM_PERM_READ, + &csp_ident))) { + csp_ident = NULL; + } + + if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow", KHM_PERM_READ, + &csp_cw))) { + if (csp_ident) { + khc_shadow_space(csp_ident, + csp_cw); + khc_close_space(csp_cw); + } else { + csp_ident = csp_cw; + } + csp_cw = NULL; + } else { +#ifdef DEBUG + assert(FALSE); +#endif + d->saved.monitor = TRUE; + d->saved.auto_renew = TRUE; + d->saved.sticky = FALSE; + d->work = d->saved; + + if (csp_ident) + khc_close_space(csp_ident); + + return; + } + + if (KHM_FAILED(khc_read_int32(csp_ident, L"Monitor", &t))) { +#ifdef DEBUG + assert(FALSE); +#endif + d->saved.monitor = TRUE; + } else { + d->saved.monitor = !!t; + } + + if (KHM_FAILED(khc_read_int32(csp_ident, L"AllowAutoRenew", &t))) { +#ifdef DEBUG + assert(FALSE); +#endif + d->saved.auto_renew = TRUE; + } else { + d->saved.auto_renew = !!t; + } + + if (KHM_FAILED(khc_read_int32(csp_ident, L"Sticky", &t))) { + d->saved.sticky = FALSE; + } else { + d->saved.sticky = !!t; + } + + khc_close_space(csp_ident); + + d->work = d->saved; + d->applied = FALSE; +} + +static void +write_params_ident(ident_data * d) { + khm_handle csp_ident; + + if (d->saved.monitor == d->work.monitor && + d->saved.auto_renew == d->work.auto_renew && + d->saved.sticky == d->work.sticky && + !d->removed) + return; + + if (KHM_FAILED(kcdb_identity_get_config(d->ident, KHM_PERM_WRITE, + &csp_ident))) { +#ifdef DEBUG + assert(FALSE); +#endif + return; + } + + if (d->saved.monitor != d->work.monitor) + khc_write_int32(csp_ident, L"Monitor", !!d->work.monitor); + + if (d->saved.auto_renew != d->work.auto_renew) + khc_write_int32(csp_ident, L"AllowAutoRenew", !!d->work.auto_renew); + + if (d->saved.sticky != d->work.sticky) { + khc_write_int32(csp_ident, L"Sticky", !!d->work.sticky); + if (d->work.sticky) { + kcdb_identity_set_flags(d->ident, KCDB_IDENT_FLAG_STICKY); + } else { + kcdb_identity_set_flags(d->ident, + KCDB_IDENT_FLAG_STICKY | + KCDB_IDENT_FLAG_INVERT); + } + } + + khc_close_space(csp_ident); + + d->saved = d->work; + + d->applied = TRUE; + + if (d->hwnd) + PostMessage(d->hwnd, KHUI_WM_CFG_NOTIFY, + MAKEWPARAM(0, WMCFG_UPDATE_STATE), 0); +} + +static void +write_params_idents(void) { + int i; + + for (i=0; i < (int)cfg_idents.n_idents; i++) { + write_params_ident(&cfg_idents.idents[i]); + } + + if (cfg_idents.hwnd) + PostMessage(cfg_idents.hwnd, KHUI_WM_CFG_NOTIFY, + MAKEWPARAM(0, WMCFG_UPDATE_STATE), 0); +} + +static void +init_idents_data(void) { + khm_int32 rv; + wchar_t * t; + wchar_t * widnames = NULL; + khm_size cb; + int n_tries = 0; + int i; + + if (cfg_idents.valid) + return; + +#ifdef DEBUG + assert(cfg_idents.idents == NULL); + assert(cfg_idents.n_idents == 0); + assert(cfg_idents.nc_idents == 0); +#endif + + do { + rv = kcdb_identity_enum(KCDB_IDENT_FLAG_CONFIG, + KCDB_IDENT_FLAG_CONFIG, + NULL, + &cb, + &cfg_idents.n_idents); + + if (rv != KHM_ERROR_TOO_LONG || + cfg_idents.n_idents == 0 || + cb == 0) + break; + + if (widnames) + free(widnames); + widnames = malloc(cb); +#ifdef DEBUG + assert(widnames); +#endif + + rv = kcdb_identity_enum(KCDB_IDENT_FLAG_CONFIG, + KCDB_IDENT_FLAG_CONFIG, + widnames, + &cb, + &cfg_idents.n_idents); + n_tries++; + } while(KHM_FAILED(rv) && + n_tries < 5); + + if (KHM_FAILED(rv) || + cfg_idents.n_idents == 0) { + cfg_idents.n_idents = 0; + goto _cleanup; + } + + cfg_idents.idents = malloc(sizeof(*cfg_idents.idents) * + cfg_idents.n_idents); +#ifdef DEBUG + assert(cfg_idents.idents); +#endif + ZeroMemory(cfg_idents.idents, + sizeof(*cfg_idents.idents) * cfg_idents.n_idents); + cfg_idents.nc_idents = cfg_idents.n_idents; + + i = 0; + for (t = widnames; t && *t; t = multi_string_next(t)) { + khm_handle ident; + + if (KHM_FAILED(kcdb_identity_create(t, 0, &ident))) { + cfg_idents.n_idents--; + continue; + } + + StringCbLength(t, KCDB_IDENT_MAXCB_NAME, &cb); + cb += sizeof(wchar_t); + + cfg_idents.idents[i].idname = malloc(cb); +#ifdef DEBUG + assert(cfg_idents.idents[i].idname); +#endif + StringCbCopy(cfg_idents.idents[i].idname, cb, t); + + cfg_idents.idents[i].ident = ident; + cfg_idents.idents[i].removed = FALSE; + + kcdb_identity_get_flags(ident, &cfg_idents.idents[i].flags); + + read_params_ident(&cfg_idents.idents[i]); + + i++; + /* leave identity held */ + } + + _cleanup: + + cfg_idents.valid = TRUE; + + if (widnames) + free(widnames); +} + +static void +free_idents_data(void) { + int i; + + if (!cfg_idents.valid) + return; + + for (i=0; i< (int) cfg_idents.n_idents; i++) { + if (cfg_idents.idents[i].ident) + kcdb_identity_release(cfg_idents.idents[i].ident); + if (cfg_idents.idents[i].idname) + free(cfg_idents.idents[i].idname); + } + + if (cfg_idents.idents) + free(cfg_idents.idents); + + cfg_idents.idents = NULL; + cfg_idents.n_idents = 0; + cfg_idents.nc_idents = 0; + cfg_idents.valid = FALSE; +} + +static void +hold_idents_data(void) { + if (!cfg_idents.valid) + init_idents_data(); +#ifdef DEBUG + assert(cfg_idents.valid); +#endif + cfg_idents.refcount++; +} + +static void +release_idents_data(void) { +#ifdef DEBUG + assert(cfg_idents.valid); +#endif + cfg_idents.refcount--; + + if (cfg_idents.refcount == 0) + free_idents_data(); +} + +#define BS_TRUE 1 +#define BS_FALSE 2 + +static void +refresh_view_idents_sel(HWND hwnd) { + HWND hw; + int sel_count; + int i; + int idx; + ident_data * d; + LVITEM lvi; + + int monitor = 0; + int auto_renew = 0; + int sticky = 0; + + hw = GetDlgItem(hwnd, IDC_CFG_IDENTS); + + sel_count = ListView_GetSelectedCount(hw); + + idx = -1; + for (i=0; i < sel_count; i++) { + idx = ListView_GetNextItem(hw, idx, LVNI_SELECTED); +#ifdef DEBUG + assert(idx != -1); +#endif + ZeroMemory(&lvi, sizeof(lvi)); + + lvi.iItem = idx; + lvi.iSubItem = 0; + lvi.mask = LVIF_PARAM; + + ListView_GetItem(hw, &lvi); + + d = (ident_data *) lvi.lParam; +#ifdef DEBUG + assert(d != NULL); +#endif + + if (d->work.monitor) + monitor |= BS_TRUE; + else + monitor |= BS_FALSE; + + if (d->work.auto_renew) + auto_renew |= BS_TRUE; + else + auto_renew |= BS_FALSE; + + if (d->work.sticky) + sticky |= BS_TRUE; + else + sticky |= BS_FALSE; + } + + CheckDlgButton(hwnd, IDC_CFG_MONITOR, + (monitor == BS_TRUE)? BST_CHECKED: + ((monitor == BS_FALSE)? BST_UNCHECKED: + BST_INDETERMINATE)); + + CheckDlgButton(hwnd, IDC_CFG_RENEW, + (auto_renew == BS_TRUE)? BST_CHECKED: + ((auto_renew == BS_FALSE)? BST_UNCHECKED: + BST_INDETERMINATE)); + + CheckDlgButton(hwnd, IDC_CFG_STICKY, + (sticky == BS_TRUE)? BST_CHECKED: + ((sticky == BS_FALSE)? BST_UNCHECKED: + BST_INDETERMINATE)); + + if (sel_count > 0) { + EnableWindow(GetDlgItem(hwnd, IDC_CFG_REMOVE), TRUE); + } else { + EnableWindow(GetDlgItem(hwnd, IDC_CFG_REMOVE), FALSE); + } +} + +#undef BS_TRUE +#undef BS_FALSE + +static void +refresh_data_idents(HWND hwnd) { + HWND hw; + int sel_count; + int i; + int idx; + ident_data * d; + LVITEM lvi; + + UINT monitor = IsDlgButtonChecked(hwnd, IDC_CFG_MONITOR); + UINT auto_renew = IsDlgButtonChecked(hwnd, IDC_CFG_RENEW); + UINT sticky = IsDlgButtonChecked(hwnd, IDC_CFG_STICKY); + + hw = GetDlgItem(hwnd, IDC_CFG_IDENTS); + + sel_count = ListView_GetSelectedCount(hw); + + idx = -1; + for (i=0; i < sel_count; i++) { + idx = ListView_GetNextItem(hw, idx, LVNI_SELECTED); +#ifdef DEBUG + assert(idx != -1); +#endif + ZeroMemory(&lvi, sizeof(lvi)); + + lvi.iItem = idx; + lvi.iSubItem = 0; + lvi.mask = LVIF_PARAM; + + ListView_GetItem(hw, &lvi); + + d = (ident_data *) lvi.lParam; +#ifdef DEBUG + assert(d != NULL); +#endif + + if (monitor == BST_CHECKED) + d->work.monitor = TRUE; + else if (monitor == BST_UNCHECKED) + d->work.monitor = FALSE; + + if (auto_renew == BST_CHECKED) + d->work.auto_renew = TRUE; + else if (auto_renew == BST_UNCHECKED) + d->work.auto_renew = FALSE; + + if (sticky == BST_CHECKED) + d->work.sticky = TRUE; + else if (sticky == BST_UNCHECKED) + d->work.sticky = FALSE; + + if (d->hwnd) + PostMessage(d->hwnd, KHUI_WM_CFG_NOTIFY, + MAKEWPARAM(0, WMCFG_UPDATE_STATE), 0); + } +} + +static void +refresh_view_idents_state(HWND hwnd) { + HWND hw; + int i; + LVITEM lvi; + ident_data * d; + + BOOL modified = FALSE; + BOOL applied = FALSE; + + hw = GetDlgItem(hwnd, IDC_CFG_IDENTS); + + for (i = -1;;) { + + i = ListView_GetNextItem(hw, i, LVNI_ALL); + if (i == -1) + break; + + ZeroMemory(&lvi, sizeof(lvi)); + lvi.iItem = i; + lvi.iSubItem = 0; + lvi.mask = LVIF_PARAM; + + ListView_GetItem(hw, &lvi); + + d = (ident_data *) lvi.lParam; +#ifdef DEBUG + assert(d != NULL); +#endif + + ZeroMemory(&lvi, sizeof(lvi)); + + lvi.mask = LVIF_STATE; + lvi.stateMask = LVIS_STATEIMAGEMASK; + lvi.iItem = i; + lvi.iSubItem = 0; + + if (d->removed) { + lvi.state = INDEXTOSTATEIMAGEMASK(cfg_idents.idx_deleted); + modified = TRUE; + } else if (d->saved.monitor != d->work.monitor || + d->saved.auto_renew != d->work.auto_renew || + d->saved.sticky != d->work.sticky) { + lvi.state = INDEXTOSTATEIMAGEMASK(cfg_idents.idx_modified); + modified = TRUE; + } else if (d->applied) { + lvi.state = INDEXTOSTATEIMAGEMASK(cfg_idents.idx_applied); + applied = TRUE; + } else { + lvi.state = INDEXTOSTATEIMAGEMASK(cfg_idents.idx_default); + } + + ListView_SetItem(hw, &lvi); + } + + { + khm_int32 flags = 0; + khui_config_node node = NULL; + + if (modified) + flags |= KHUI_CNFLAG_MODIFIED; + if (applied) + flags |= KHUI_CNFLAG_APPLIED; + + khui_cfg_open(NULL, L"KhmIdentities", &node); +#ifdef DEBUG + assert(node); +#endif + khui_cfg_set_flags(node, flags, + KHUI_CNFLAG_APPLIED | KHUI_CNFLAG_MODIFIED); + } +} + +static void +remove_idents(HWND hwnd) { + HWND hw; + int sel_count; + int i; + int idx; + ident_data * d; + LVITEM lvi; + + hw = GetDlgItem(hwnd, IDC_CFG_IDENTS); + + sel_count = ListView_GetSelectedCount(hw); + + idx = -1; + for (i=0; i < sel_count; i++) { + idx = ListView_GetNextItem(hw, idx, LVNI_SELECTED); +#ifdef DEBUG + assert(idx != -1); +#endif + ZeroMemory(&lvi, sizeof(lvi)); + + lvi.iItem = idx; + lvi.iSubItem = 0; + lvi.mask = LVIF_PARAM; + + ListView_GetItem(hw, &lvi); + + d = (ident_data *) lvi.lParam; +#ifdef DEBUG + assert(d != NULL); +#endif + + d->removed = TRUE; + } +} + +INT_PTR CALLBACK +khm_cfg_ids_tab_proc(HWND hwnd, + UINT umsg, + WPARAM wParam, + LPARAM lParam) { + + switch(umsg) { + case WM_INITDIALOG: + { + HWND hw; + HICON hicon; + LVCOLUMN lvcol; + LVITEM lvi; + wchar_t coltext[256]; + RECT r; + int i; + + hold_idents_data(); + + cfg_idents.hwnd = hwnd; + + /* first add the column */ + hw = GetDlgItem(hwnd, IDC_CFG_IDENTS); + + ZeroMemory(&lvcol, sizeof(lvcol)); + lvcol.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_FMT; + + lvcol.fmt = LVCFMT_IMAGE | LVCFMT_LEFT; + + GetWindowRect(hw, &r); + lvcol.cx = ((r.right - r.left) * 95) / 100; + + LoadString(khm_hInstance, IDS_CFG_IDS_IDENTITY, + coltext, ARRAYLENGTH(coltext)); + lvcol.pszText = coltext; + + ListView_InsertColumn(hw, 0, &lvcol); + + /* and the status icons */ + if (cfg_idents.hi_status) + goto _done_with_icons; + + cfg_idents.hi_status = ImageList_Create(SM_CXICON, SM_CYICON, + ILC_COLOR8 | ILC_MASK, + 4,4); + + hicon = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_ID), + IMAGE_ICON, SM_CXICON, SM_CYICON, LR_DEFAULTCOLOR); + + cfg_idents.idx_id = ImageList_AddIcon(cfg_idents.hi_status, + hicon); + + DestroyIcon(hicon); + + hicon = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_CFG_DEFAULT), + IMAGE_ICON, SM_CXICON, SM_CYICON, LR_DEFAULTCOLOR); + + cfg_idents.idx_default = ImageList_AddIcon(cfg_idents.hi_status, + hicon) + 1; + + DestroyIcon(hicon); + + hicon = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_CFG_MODIFIED), + IMAGE_ICON, SM_CXICON, SM_CYICON, LR_DEFAULTCOLOR); + + cfg_idents.idx_modified = ImageList_AddIcon(cfg_idents.hi_status, + hicon) + 1; + + DestroyIcon(hicon); + + hicon = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_CFG_APPLIED), + IMAGE_ICON, SM_CXICON, SM_CYICON, LR_DEFAULTCOLOR); + + cfg_idents.idx_applied = ImageList_AddIcon(cfg_idents.hi_status, + hicon) + 1; + + DestroyIcon(hicon); + + hicon = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_CFG_DELETED), + IMAGE_ICON, SM_CXICON, SM_CYICON, LR_DEFAULTCOLOR); + + cfg_idents.idx_deleted = ImageList_AddIcon(cfg_idents.hi_status, + hicon) + 1; + + DestroyIcon(hicon); + + ListView_SetImageList(hw, cfg_idents.hi_status, LVSIL_SMALL); + ListView_SetImageList(hw, cfg_idents.hi_status, LVSIL_STATE); + + _done_with_icons: + + /* now add each identity */ + for(i=0; i < (int)cfg_idents.n_idents; i++) { + ZeroMemory(&lvi, sizeof(lvi)); + + lvi.mask = LVIF_PARAM | LVIF_TEXT | LVIF_STATE | LVIF_IMAGE; + lvi.iImage = cfg_idents.idx_id; + lvi.lParam = (LPARAM) &cfg_idents.idents[i]; + lvi.pszText = cfg_idents.idents[i].idname; + lvi.state = INDEXTOSTATEIMAGEMASK(cfg_idents.idx_default); + lvi.stateMask = LVIS_STATEIMAGEMASK; + + cfg_idents.idents[i].lv_idx = ListView_InsertItem(hw, &lvi); + } + + ListView_SetView(hw, LV_VIEW_DETAILS); + } + return FALSE; + + case WM_NOTIFY: + { + LPNMHDR lpnm = (LPNMHDR) lParam; + + if (lpnm->code == LVN_ITEMCHANGED) { + refresh_view_idents_sel(hwnd); + } + } + return TRUE; + + case WM_COMMAND: + + if (HIWORD(wParam) == BN_CLICKED) { + UINT ctrl = LOWORD(wParam); + switch(ctrl) { + case IDC_CFG_MONITOR: + case IDC_CFG_RENEW: + case IDC_CFG_STICKY: + if (IsDlgButtonChecked(hwnd, ctrl) == BST_CHECKED) + CheckDlgButton(hwnd, ctrl, BST_UNCHECKED); + else + CheckDlgButton(hwnd, ctrl, BST_CHECKED); + refresh_data_idents(hwnd); + break; + + case IDC_CFG_REMOVE: + remove_idents(hwnd); + break; + } + + refresh_view_idents_state(hwnd); + } + + khm_set_dialog_result(hwnd, 0); + return TRUE; + + case KHUI_WM_CFG_NOTIFY: + { + switch(HIWORD(wParam)) { + case WMCFG_APPLY: + write_params_idents(); + break; + + case WMCFG_UPDATE_STATE: + refresh_view_idents_state(hwnd); + refresh_view_idents_sel(hwnd); + break; + } + } + return TRUE; + + case WM_DESTROY: + cfg_idents.hwnd = NULL; + + if (cfg_idents.hi_status != NULL) { + ImageList_Destroy(cfg_idents.hi_status); + cfg_idents.hi_status = NULL; + } + release_idents_data(); + + khm_set_dialog_result(hwnd, 0); + + return TRUE; + } + + return FALSE; +} + +INT_PTR CALLBACK +khm_cfg_identities_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + HWND hw; + switch(uMsg) { + case WM_INITDIALOG: + set_window_node(hwnd, (khui_config_node) lParam); + add_subpanels(hwnd, (khui_config_node) lParam, + (khui_config_node) lParam); + hw = GetDlgItem(hwnd, IDC_CFG_TAB); + show_tab_panel(hwnd, + (khui_config_node) lParam, + hw, + TabCtrl_GetCurSel(hw), + TRUE); + return FALSE; + + case WM_DESTROY: + return 0; + + case KHUI_WM_CFG_NOTIFY: + return handle_cfg_notify(hwnd, wParam, lParam); + + case WM_NOTIFY: + return handle_notify(hwnd, wParam, lParam); + } + + return FALSE; +} + +static ident_data * +find_ident_by_node(khui_config_node node) { + khm_size cb; + wchar_t idname[KCDB_IDENT_MAXCCH_NAME]; + int i; + + cb = sizeof(idname); + khui_cfg_get_name(node, idname, &cb); + + for (i=0; i < (int)cfg_idents.n_idents; i++) { + if (!wcscmp(cfg_idents.idents[i].idname, idname)) + break; + } + + if (i < (int)cfg_idents.n_idents) + return &cfg_idents.idents[i]; + else + return NULL; +} + +static void +refresh_view_ident(HWND hwnd, khui_config_node node) { + ident_data * d; + + d = find_ident_by_node(node); +#ifdef DEBUG + assert(d); +#endif + + CheckDlgButton(hwnd, IDC_CFG_MONITOR, + (d->work.monitor? BST_CHECKED: BST_UNCHECKED)); + CheckDlgButton(hwnd, IDC_CFG_RENEW, + (d->work.auto_renew? BST_CHECKED: BST_UNCHECKED)); + CheckDlgButton(hwnd, IDC_CFG_STICKY, + (d->work.sticky? BST_CHECKED: BST_UNCHECKED)); +} + +static void +refresh_data_ident(HWND hwnd, khui_config_init_data * idata) { + ident_data * d; + + d = find_ident_by_node(idata->ctx_node); +#ifdef DEBUG + assert(d); +#endif + + if (IsDlgButtonChecked(hwnd, IDC_CFG_MONITOR) == BST_CHECKED) + d->work.monitor = TRUE; + else + d->work.monitor = FALSE; + + if (IsDlgButtonChecked(hwnd, IDC_CFG_RENEW) == BST_CHECKED) + d->work.auto_renew = TRUE; + else + d->work.auto_renew = FALSE; + + if (IsDlgButtonChecked(hwnd, IDC_CFG_STICKY) == BST_CHECKED) + d->work.sticky = TRUE; + else + d->work.sticky = FALSE; + + if (d->work.monitor != d->saved.monitor || + d->work.auto_renew != d->saved.auto_renew || + d->work.sticky != d->saved.sticky) { + + khui_cfg_set_flags_inst(idata, KHUI_CNFLAG_MODIFIED, + KHUI_CNFLAG_MODIFIED); + + } else { + khui_cfg_set_flags_inst(idata, 0, + KHUI_CNFLAG_MODIFIED); + } +} + +INT_PTR CALLBACK +khm_cfg_id_tab_proc(HWND hwnd, + UINT umsg, + WPARAM wParam, + LPARAM lParam) { + + khui_config_init_data * idata; + + switch(umsg) { + case WM_INITDIALOG: + { + ident_data * d; + + hold_idents_data(); + + idata = (khui_config_init_data *) lParam; + + khui_cfg_init_dialog_data(hwnd, idata, 0, NULL, NULL); + + refresh_view_ident(hwnd, idata->ctx_node); + + d = find_ident_by_node(idata->ctx_node); + if (d) + d->hwnd = hwnd; +#ifdef DEBUG + else + assert(FALSE); +#endif + } + return FALSE; + + case WM_COMMAND: + khui_cfg_get_dialog_data(hwnd, &idata, NULL); + + if (HIWORD(wParam) == BN_CLICKED) { + switch(LOWORD(wParam)) { + case IDC_CFG_MONITOR: + case IDC_CFG_RENEW: + case IDC_CFG_STICKY: + + refresh_data_ident(hwnd, idata); + if (cfg_idents.hwnd) + PostMessage(cfg_idents.hwnd, KHUI_WM_CFG_NOTIFY, + MAKEWPARAM(1, WMCFG_UPDATE_STATE), 0); + break; + } + } + khm_set_dialog_result(hwnd, 0); + return TRUE; + + case WM_DESTROY: + { + ident_data * d; + + khui_cfg_get_dialog_data(hwnd, &idata, NULL); + + d = find_ident_by_node(idata->ctx_node); + if (d) + d->hwnd = NULL; + + release_idents_data(); + khui_cfg_free_dialog_data(hwnd); + khm_set_dialog_result(hwnd, 0); + } + return TRUE; + + case KHUI_WM_CFG_NOTIFY: + { + ident_data * d; + + khui_cfg_get_dialog_data(hwnd, &idata, NULL); + + switch (HIWORD(wParam)) { + case WMCFG_APPLY: + d = find_ident_by_node(idata->ctx_node); + write_params_ident(d); + khui_cfg_set_flags_inst(idata, KHUI_CNFLAG_APPLIED, + KHUI_CNFLAG_APPLIED | + KHUI_CNFLAG_MODIFIED); + break; + + case WMCFG_UPDATE_STATE: + refresh_view_ident(hwnd, idata->ctx_node); + refresh_data_ident(hwnd, idata); + break; + } + } + return TRUE; + } + + return FALSE; +} + +INT_PTR CALLBACK +khm_cfg_identity_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + HWND hw; + + switch(uMsg) { + case WM_INITDIALOG: + { + khui_config_node refnode = NULL; + + set_window_node(hwnd, (khui_config_node) lParam); + + khui_cfg_open(NULL, L"KhmIdentities", &refnode); +#ifdef DEBUG + assert(refnode != NULL); +#endif + add_subpanels(hwnd, + (khui_config_node) lParam, + refnode); + + hw = GetDlgItem(hwnd, IDC_CFG_TAB); + + show_tab_panel(hwnd, + (khui_config_node) lParam, + hw, + TabCtrl_GetCurSel(hw), + TRUE); + + khui_cfg_release(refnode); + } + return FALSE; + + case WM_DESTROY: + return 0; + + case KHUI_WM_CFG_NOTIFY: + return handle_cfg_notify(hwnd, wParam, lParam); + + case WM_NOTIFY: + return handle_notify(hwnd, wParam, lParam); + } + return FALSE; +} diff --git a/src/windows/identity/ui/cfg_notif_wnd.c b/src/windows/identity/ui/cfg_notif_wnd.c new file mode 100644 index 000000000..aafa12d72 --- /dev/null +++ b/src/windows/identity/ui/cfg_notif_wnd.c @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khmapp.h> +#include<assert.h> + +typedef struct tag_notif_data { + khui_config_node node; + + BOOL monitor; + BOOL renew; + BOOL warn1; + BOOL warn2; + + khui_tracker tc_renew; + khui_tracker tc_warn1; + khui_tracker tc_warn2; +} notif_data; + +static void +read_params(notif_data * d) { + khm_handle csp_cw; + khm_int32 rv; + khm_int32 t; + + rv = khc_open_space(NULL, L"CredWindow", KHM_PERM_READ, &csp_cw); + assert(KHM_SUCCEEDED(rv)); + + rv = khc_read_int32(csp_cw, L"Monitor", &t); + assert(KHM_SUCCEEDED(rv)); + d->monitor = !!t; + + rv = khc_read_int32(csp_cw, L"AllowAutoRenew", &t); + assert(KHM_SUCCEEDED(rv)); + d->renew = !!t; + + rv = khc_read_int32(csp_cw, L"AllowWarn", &t); + assert(KHM_SUCCEEDED(rv)); + d->warn1 = !!t; + + rv = khc_read_int32(csp_cw, L"AllowCritical", &t); + assert(KHM_SUCCEEDED(rv)); + d->warn2 = !!t; + + rv = khc_read_int32(csp_cw, L"AutoRenewThreshold", &t); + assert(KHM_SUCCEEDED(rv)); + d->tc_renew.current = t; + + rv = khc_read_int32(csp_cw, L"WarnThreshold", &t); + assert(KHM_SUCCEEDED(rv)); + d->tc_warn1.current = t; + + rv = khc_read_int32(csp_cw, L"CriticalThreshold", &t); + assert(KHM_SUCCEEDED(rv)); + d->tc_warn2.current = t; + + rv = khc_read_int32(csp_cw, L"MaxThreshold", &t); + assert(KHM_SUCCEEDED(rv)); + d->tc_renew.max = t; + d->tc_warn1.max = t; + d->tc_warn2.max = t; + + rv = khc_read_int32(csp_cw, L"MinThreshold", &t); + assert(KHM_SUCCEEDED(rv)); + d->tc_renew.min = t; + d->tc_warn1.min = t; + d->tc_warn2.min = t; + + khc_close_space(csp_cw); +} + +static void +check_for_modification(notif_data * d) { + notif_data t; + + ZeroMemory(&t, sizeof(t)); + + read_params(&t); + + if ((!!d->monitor) != (!!t.monitor) || + (!!d->renew) != (!!t.renew) || + (!!d->warn1) != (!!t.warn1) || + (!!d->warn2) != (!!t.warn2) || + d->tc_renew.current != t.tc_renew.current || + d->tc_warn1.current != t.tc_warn1.current || + d->tc_warn2.current != t.tc_warn2.current) { + + khui_cfg_set_flags(d->node, + KHUI_CNFLAG_MODIFIED, + KHUI_CNFLAG_MODIFIED); + + } else { + khui_cfg_set_flags(d->node, + 0, + KHUI_CNFLAG_MODIFIED); + } +} + +static void +write_params(notif_data * d) { + khm_handle csp_cw; + khm_int32 rv; + + rv = khc_open_space(NULL, L"CredWindow", KHM_PERM_WRITE, &csp_cw); + assert(KHM_SUCCEEDED(rv)); + + rv = khc_write_int32(csp_cw, L"Monitor", d->monitor); + assert(KHM_SUCCEEDED(rv)); + + rv = khc_write_int32(csp_cw, L"AllowAutoRenew", d->renew); + assert(KHM_SUCCEEDED(rv)); + + rv = khc_write_int32(csp_cw, L"AllowWarn", d->warn1); + assert(KHM_SUCCEEDED(rv)); + + rv = khc_write_int32(csp_cw, L"AllowCritical", d->warn2); + assert(KHM_SUCCEEDED(rv)); + + + rv = khc_write_int32(csp_cw, L"AutoRenewThreshold", + (khm_int32) d->tc_renew.current); + assert(KHM_SUCCEEDED(rv)); + + rv = khc_write_int32(csp_cw, L"WarnThreshold", + (khm_int32) d->tc_warn1.current); + assert(KHM_SUCCEEDED(rv)); + + rv = khc_write_int32(csp_cw, L"CriticalThreshold", + (khm_int32) d->tc_warn2.current); + assert(KHM_SUCCEEDED(rv)); + + khc_close_space(csp_cw); + + khui_cfg_set_flags(d->node, + KHUI_CNFLAG_APPLIED, + KHUI_CNFLAG_APPLIED | KHUI_CNFLAG_MODIFIED); +} + +static void +refresh_view(HWND hwnd, notif_data * d) { + CheckDlgButton(hwnd, IDC_NOTIF_MONITOR, + (d->monitor?BST_CHECKED:BST_UNCHECKED)); + CheckDlgButton(hwnd, IDC_NOTIF_RENEW, + (d->renew?BST_CHECKED:BST_UNCHECKED)); + CheckDlgButton(hwnd, IDC_NOTIF_WARN1, + (d->warn1?BST_CHECKED:BST_UNCHECKED)); + CheckDlgButton(hwnd, IDC_NOTIF_WARN2, + (d->warn2?BST_CHECKED:BST_UNCHECKED)); + khui_tracker_refresh(&d->tc_renew); + khui_tracker_refresh(&d->tc_warn1); + khui_tracker_refresh(&d->tc_warn2); + if (!d->monitor) { + EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_RENEW), FALSE); + EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_WARN1), FALSE); + EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_WARN2), FALSE); + EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_RENEW_THR), FALSE); + EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_WARN1_THR), FALSE); + EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_WARN2_THR), FALSE); + } else { + EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_RENEW), TRUE); + EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_WARN1), TRUE); + EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_WARN2), TRUE); + EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_RENEW_THR), !!(d->renew)); + EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_WARN1_THR), !!(d->warn1)); + EnableWindow(GetDlgItem(hwnd, IDC_NOTIF_WARN2_THR), !!(d->warn2)); + } +} + +static void +refresh_data(HWND hwnd, notif_data * d) { + d->monitor = (IsDlgButtonChecked(hwnd, IDC_NOTIF_MONITOR) + == BST_CHECKED); + d->renew = (IsDlgButtonChecked(hwnd, IDC_NOTIF_RENEW) + == BST_CHECKED); + d->warn1 = (IsDlgButtonChecked(hwnd, IDC_NOTIF_WARN1) + == BST_CHECKED); + d->warn2 = (IsDlgButtonChecked(hwnd, IDC_NOTIF_WARN2) + == BST_CHECKED); + + check_for_modification(d); +} + +INT_PTR CALLBACK +khm_cfg_notifications_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + + notif_data * d; + + switch(uMsg) { + case WM_INITDIALOG: { + HWND hw; + + d = malloc(sizeof(*d)); +#ifdef DEBUG + assert(d != NULL); +#endif + +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d); +#pragma warning(pop) + + ZeroMemory(d, sizeof(*d)); + + d->node = (khui_config_node) lParam; + + khui_tracker_initialize(&d->tc_renew); + khui_tracker_initialize(&d->tc_warn1); + khui_tracker_initialize(&d->tc_warn2); + + read_params(d); + + hw = GetDlgItem(hwnd, IDC_NOTIF_RENEW_THR); + khui_tracker_install(hw, &d->tc_renew); + + hw = GetDlgItem(hwnd, IDC_NOTIF_WARN1_THR); + khui_tracker_install(hw, &d->tc_warn1); + + hw = GetDlgItem(hwnd, IDC_NOTIF_WARN2_THR); + khui_tracker_install(hw, &d->tc_warn2); + + refresh_view(hwnd, d); + + /* normally we should return TRUE, but in this case we return + FALSE since we don't want to inadvertently steal the focus + from the treeview. */ + return FALSE; + } + + case WM_COMMAND: { + d = (notif_data *) (DWORD_PTR) GetWindowLongPtr(hwnd, DWLP_USER); + + if (HIWORD(wParam) == BN_CLICKED) { + refresh_data(hwnd, d); + refresh_view(hwnd, d); + + check_for_modification(d); + } else if (HIWORD(wParam) == EN_CHANGE) { + SetTimer(hwnd, 1, 500, NULL); + } + + khm_set_dialog_result(hwnd, 0); + + return TRUE; + } + + case WM_TIMER: { + d = (notif_data *) (DWORD_PTR) GetWindowLongPtr(hwnd, DWLP_USER); + KillTimer(hwnd, 1); + check_for_modification(d); + + khm_set_dialog_result(hwnd, 0); + + return TRUE; + } + + case WM_DESTROY: { + d = (notif_data *) (DWORD_PTR) GetWindowLongPtr(hwnd, DWLP_USER); + + khui_tracker_kill_controls(&d->tc_renew); + khui_tracker_kill_controls(&d->tc_warn1); + khui_tracker_kill_controls(&d->tc_warn2); + + free(d); + + SetWindowLongPtr(hwnd, DWLP_USER, 0); + + khm_set_dialog_result(hwnd, 0); + + return TRUE; + } + + case KHUI_WM_CFG_NOTIFY: { + d = (notif_data *) (DWORD_PTR) GetWindowLongPtr(hwnd, DWLP_USER); + + if (HIWORD(wParam) == WMCFG_APPLY) { + write_params(d); + } + + khm_set_dialog_result(hwnd, 0); + + return TRUE; + } + + } + + return FALSE; +} diff --git a/src/windows/identity/ui/cfg_plugins_wnd.c b/src/windows/identity/ui/cfg_plugins_wnd.c new file mode 100644 index 000000000..16a344275 --- /dev/null +++ b/src/windows/identity/ui/cfg_plugins_wnd.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khmapp.h> +#include<assert.h> + +#define MAX_PLUGINS 256 + +typedef struct tag_plugin_data { + kmm_plugin_info plugin; + kmm_module_info module; +} plugin_data; + +typedef struct tag_plugin_dlg_data { + plugin_data * info[MAX_PLUGINS]; + khm_size n_info; +} plugin_dlg_data; + +INT_PTR CALLBACK +khm_cfg_plugins_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + + plugin_dlg_data * d; + + switch(uMsg) { + case WM_INITDIALOG: + { + kmm_plugin p; + kmm_plugin pn; + kmm_module m; + khm_size i; + LVCOLUMN lvc; + RECT r; + HWND hw; + wchar_t buf[256]; + + + d = malloc(sizeof(*d)); +#ifdef DEBUG + assert(d); +#endif + ZeroMemory(d, sizeof(*d)); +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d); +#pragma warning(pop) + + p = NULL; + i = 0; + do { + if (KHM_FAILED(kmm_get_next_plugin(p, &pn))) + break; + + if (p) + kmm_release_plugin(p); + p = pn; + +#ifdef DEBUG + assert(d->info[i] == NULL); +#endif + d->info[i] = malloc(sizeof(*(d->info[i]))); +#ifdef DEBUG + assert(d->info[i]); +#endif + + if (KHM_FAILED(kmm_get_plugin_info_i(p, &d->info[i]->plugin))) { + free(d->info[i]); + d->info[i] = NULL; + break; + } + + ZeroMemory(&d->info[i]->module, + sizeof(d->info[i]->module)); + + if (KHM_SUCCEEDED(kmm_load_module(d->info[i]->plugin.reg.module, + KMM_LM_FLAG_NOLOAD, + &m))) { + kmm_get_module_info_i(m, &d->info[i]->module); + kmm_release_module(m); + } + + i ++; + + if (i == MAX_PLUGINS) + break; + } while(p); + + if (p) + kmm_release_plugin(p); + + d->n_info = i; + + /* now populate the list view */ + hw = GetDlgItem(hwnd, IDC_CFG_PLUGINS); +#ifdef DEBUG + assert(hw); +#endif + ListView_SetView(hw, LV_VIEW_DETAILS); + + ZeroMemory(&lvc, sizeof(lvc)); + + lvc.mask = LVCF_TEXT | LVCF_WIDTH; + GetWindowRect(hw, &r); + lvc.cx = ((r.right - r.left) * 95) / 100; + lvc.pszText = buf; + + LoadString(khm_hInstance, IDS_CFG_PI_COL_PLUGINS, + buf, ARRAYLENGTH(buf)); + + ListView_InsertColumn(hw, 0, &lvc); + + for(i=0; i<d->n_info; i++) { + LVITEM lvi; + + ZeroMemory(&lvi, sizeof(lvi)); + + lvi.mask = LVIF_PARAM | LVIF_TEXT; + lvi.lParam = (LPARAM) d->info[i]; + lvi.pszText = d->info[i]->plugin.reg.name; + + ListView_InsertItem(hw, &lvi); + } + } + return FALSE; + + case WM_NOTIFY: + { + LPNMHDR lpnm; + HWND hw; + + d = (plugin_dlg_data *) (LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + + if (wParam == IDC_CFG_PLUGINS && + (lpnm = (LPNMHDR) lParam) && + lpnm->code == LVN_ITEMCHANGED) { + + LVITEM lvi; + + hw = GetDlgItem(hwnd, IDC_CFG_PLUGINS); +#ifdef DEBUG + assert(hw); +#endif + if (ListView_GetSelectedCount(hw) != 1) { + SetDlgItemText(hwnd, IDC_CFG_DESC, L""); + SetDlgItemText(hwnd, IDC_CFG_STATE, L""); + SetDlgItemText(hwnd, IDC_CFG_MODULE, L""); + SetDlgItemText(hwnd, IDC_CFG_VENDOR, L""); + EnableWindow(GetDlgItem(hwnd, IDC_CFG_ENABLE), FALSE); + EnableWindow(GetDlgItem(hwnd, IDC_CFG_DISABLE), FALSE); + SendDlgItemMessage(hwnd, IDC_CFG_DEPS, + LB_RESETCONTENT, 0, 0); + } else { + int idx; + plugin_data * info; + wchar_t buf[256]; + UINT resid; + wchar_t * t; + + idx = ListView_GetNextItem(hw, -1, LVNI_SELECTED); +#ifdef DEBUG + assert(idx != -1); +#endif + ZeroMemory(&lvi, sizeof(lvi)); + lvi.iItem = idx; + lvi.iSubItem = 0; + lvi.mask = LVIF_PARAM; + + ListView_GetItem(hw, &lvi); +#ifdef DEBUG + assert(lvi.lParam != 0); +#endif + info = (plugin_data *) lvi.lParam; + + if (info->plugin.reg.description) + SetDlgItemText(hwnd, IDC_CFG_DESC, info->plugin.reg.description); + else + SetDlgItemText(hwnd, IDC_CFG_DESC, L""); + + switch(info->plugin.state) { + case KMM_PLUGIN_STATE_FAIL_UNKNOWN: + resid = IDS_PISTATE_FAILUNK; + break; + + case KMM_PLUGIN_STATE_FAIL_MAX_FAILURE: + resid = IDS_PISTATE_FAILMAX; + break; + + case KMM_PLUGIN_STATE_FAIL_NOT_REGISTERED: + resid = IDS_PISTATE_FAILREG; + break; + + case KMM_PLUGIN_STATE_FAIL_DISABLED: + resid = IDS_PISTATE_FAILDIS; + break; + + case KMM_PLUGIN_STATE_FAIL_LOAD: + resid = IDS_PISTATE_FAILLOD; + break; + + case KMM_PLUGIN_STATE_NONE: + case KMM_PLUGIN_STATE_PLACEHOLDER: + resid = IDS_PISTATE_PLACEHOLD; + break; + + case KMM_PLUGIN_STATE_REG: + case KMM_PLUGIN_STATE_PREINIT: + resid = IDS_PISTATE_REG; + break; + + case KMM_PLUGIN_STATE_HOLD: + resid = IDS_PISTATE_HOLD; + break; + + case KMM_PLUGIN_STATE_INIT: + resid = IDS_PISTATE_INIT; + break; + + case KMM_PLUGIN_STATE_RUNNING: + resid = IDS_PISTATE_RUN; + break; + + case KMM_PLUGIN_STATE_EXITED: + resid = IDS_PISTATE_EXIT; + break; + + default: +#ifdef DEBUG + assert(FALSE); +#endif + resid = IDS_PISTATE_FAILUNK; + } + + LoadString(khm_hInstance, resid, + buf, ARRAYLENGTH(buf)); + + SetDlgItemText(hwnd, IDC_CFG_STATE, buf); + + SendDlgItemMessage(hwnd, IDC_CFG_DEPS, + LB_RESETCONTENT, 0, 0); + + for (t = info->plugin.reg.dependencies; t && *t; + t = multi_string_next(t)) { + SendDlgItemMessage(hwnd, IDC_CFG_DEPS, + LB_INSERTSTRING, + -1, + (LPARAM) t); + } + + if (info->plugin.reg.module) + SetDlgItemText(hwnd, IDC_CFG_MODULE, + info->plugin.reg.module); + else + SetDlgItemText(hwnd, IDC_CFG_MODULE, + L""); + + if (info->module.reg.vendor) + SetDlgItemText(hwnd, IDC_CFG_VENDOR, + info->module.reg.vendor); + else + SetDlgItemText(hwnd, IDC_CFG_VENDOR, + L""); + } + } + } + return TRUE; + + case WM_DESTROY: + { + khm_size i; + + d = (plugin_dlg_data *) (LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); +#ifdef DEBUG + assert(d); +#endif + for (i=0; i<d->n_info; i++) { +#ifdef DEBUG + assert(d->info[i]); +#endif + kmm_release_plugin_info_i(&d->info[i]->plugin); + kmm_release_module_info_i(&d->info[i]->module); + free(d->info[i]); + } + + free(d); + + khm_set_dialog_result(hwnd, 0); + } + return TRUE; + } + return FALSE; +} diff --git a/src/windows/identity/ui/configwnd.c b/src/windows/identity/ui/configwnd.c new file mode 100644 index 000000000..22a41eeb5 --- /dev/null +++ b/src/windows/identity/ui/configwnd.c @@ -0,0 +1,839 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khmapp.h> +#include<assert.h> + +static HWND cfgui_hwnd = NULL; + +typedef struct tag_cfgui_wnd_data { + khui_config_node current; + HWND hw_current; + HWND hw_generic_pane; + HBRUSH hbr_white; + HFONT hf_title; + khui_bitmap kbmp_logo; + HIMAGELIST hi_status; + BOOL modified; + int idx_default; + int idx_modified; + int idx_applied; +} cfgui_wnd_data; + +static cfgui_wnd_data * +cfgui_get_wnd_data(HWND hwnd) { + return (cfgui_wnd_data *)(LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); +} + +static void +cfgui_set_wnd_data(HWND hwnd, cfgui_wnd_data * d) { +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) d); +#pragma warning(pop) +} + +static void +cfgui_add_node(cfgui_wnd_data * d, + HWND hwtv, + khui_config_node node, + khui_config_node parent, + BOOL sorted) { + + khui_config_node_reg reg; + khui_config_node c; + wchar_t wbuf[256]; + const wchar_t * short_desc; + TVINSERTSTRUCT s; + HTREEITEM hItem; + + if (node) { + khui_cfg_get_reg(node, ®); + short_desc = reg.short_desc; + } else { + short_desc = wbuf; + LoadString(khm_hInstance, IDS_CFG_ROOT_NAME, + wbuf, ARRAYLENGTH(wbuf)); + reg.flags = 0; + } + + ZeroMemory(&s, sizeof(s)); + + s.hParent = (node)? + (HTREEITEM) khui_cfg_get_param(parent): + TVI_ROOT; + + s.hInsertAfter = (sorted)? TVI_SORT: TVI_FIRST; + + s.itemex.mask = + TVIF_CHILDREN | + TVIF_PARAM | + TVIF_TEXT | + TVIF_STATE; + + { + khui_config_node n; + + if (KHM_SUCCEEDED(khui_cfg_get_first_child(node, + &n))) { + s.itemex.cChildren = 1; + s.itemex.state = TVIS_EXPANDED; + s.itemex.stateMask = TVIS_EXPANDED; + khui_cfg_release(n); + } else { + s.itemex.cChildren = 0; + s.itemex.state = 0; + s.itemex.stateMask = TVIS_EXPANDED; + } + + s.itemex.state |= INDEXTOSTATEIMAGEMASK(d->idx_default); + s.itemex.stateMask |= TVIS_STATEIMAGEMASK; + } + + s.itemex.lParam = (LPARAM) node; + khui_cfg_hold(node); + + s.itemex.pszText = (LPWSTR) short_desc; + + hItem = TreeView_InsertItem(hwtv, &s); + + khui_cfg_set_param(node, (LPARAM) hItem); + + if (KHM_SUCCEEDED(khui_cfg_get_first_child(node, + &c))) { + do { + cfgui_add_node(d, hwtv, c, node, + !!(reg.flags & KHUI_CNFLAG_SORT_CHILDREN)); + } while (KHM_SUCCEEDED(khui_cfg_get_next_release(&c))); + } +} + +static void +cfgui_initialize_dialog(HWND hwnd) { + cfgui_wnd_data * d; + HWND hwtv; + HWND hwtitle; + HFONT hf; + HDC hdc; + HICON hicon; + + d = cfgui_get_wnd_data(hwnd); + + /* create and fill the image list for the treeview */ + + d->hi_status = ImageList_Create(SM_CXICON, SM_CYICON, + ILC_COLOR8 | ILC_MASK, + 4,4); + + hicon = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_CFG_DEFAULT), + IMAGE_ICON, SM_CXICON, SM_CYICON, LR_DEFAULTCOLOR); + + /* note that we can't use index 0 because that is used to indicate + that there is no state image for the node */ + do { + d->idx_default = ImageList_AddIcon(d->hi_status, hicon); + } while(d->idx_default == 0); + + DestroyIcon(hicon); + + hicon = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_CFG_MODIFIED), + IMAGE_ICON, SM_CXICON, SM_CYICON, LR_DEFAULTCOLOR); + + d->idx_modified = ImageList_AddIcon(d->hi_status, hicon); + + DestroyIcon(hicon); + + hicon = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_CFG_APPLIED), + IMAGE_ICON, SM_CXICON, SM_CYICON, LR_DEFAULTCOLOR); + + d->idx_applied = ImageList_AddIcon(d->hi_status, hicon); + + DestroyIcon(hicon); + + /* now for the treeview */ + hwtv = GetDlgItem(hwnd, IDC_CFG_NODELIST); + + TreeView_SetImageList(hwtv, d->hi_status, TVSIL_STATE); + + cfgui_add_node(d, hwtv, NULL, NULL, FALSE); + + hdc = GetDC(hwnd); + hf = CreateFont(-MulDiv(12, + GetDeviceCaps(hdc, LOGPIXELSY), + 72), + 0, /* nWidth */ + 0, /* nEscapement */ + 0, /* nOrientation */ + FW_BOLD, /* fnWeight */ + TRUE, /* fdwItalic */ + FALSE, /* fdwUnderline */ + FALSE, /* fdwStrikeOut */ + DEFAULT_CHARSET, /* fdwCharSet */ + OUT_DEFAULT_PRECIS, /* fdwOutputPrecision */ + CLIP_DEFAULT_PRECIS, /* fdwClipPrecision */ + DEFAULT_QUALITY, /* fdwQuality */ + FF_SWISS | DEFAULT_PITCH, /* pitch&family */ + NULL); /* face */ + ReleaseDC(hwnd, hdc); + + d->hf_title = hf; + + hwtitle = GetDlgItem(hwnd, IDC_CFG_TITLE); + + SendMessage(hwtitle, + WM_SETFONT, + (WPARAM) hf, + (LPARAM) FALSE); +} + +static void +cfgui_free_node(HWND hwtv, HTREEITEM hItem) { + TVITEMEX iex; + HTREEITEM hChItem; + + ZeroMemory(&iex, sizeof(iex)); + + iex.mask = TVIF_PARAM; + iex.hItem = hItem; + + if (TreeView_GetItem(hwtv, &iex)) { + khui_config_node node; + + node = (khui_config_node) iex.lParam; + khui_cfg_release(node); + } + + hChItem = TreeView_GetChild(hwtv, hItem); + while(hChItem) { + cfgui_free_node(hwtv, hChItem); + + hChItem = TreeView_GetNextSibling(hwtv, hChItem); + } +} + +static void +cfgui_uninitialize_dialog(HWND hwnd) { + cfgui_wnd_data * d; + HWND hwtv; + + d = cfgui_get_wnd_data(hwnd); + + hwtv = GetDlgItem(hwnd, IDC_CFG_NODELIST); + + cfgui_free_node(hwtv, TreeView_GetRoot(hwtv)); + + if (d->hf_title) + DeleteObject(d->hf_title); + + if (d->hi_status) + ImageList_Destroy(d->hi_status); +} + +static void +cfgui_activate_node(HWND hwnd, khui_config_node node) { + + cfgui_wnd_data * d; + HTREEITEM hItem; + HWND hw_new; + HWND hwtv; + + d = cfgui_get_wnd_data(hwnd); + hwtv = GetDlgItem(hwnd, IDC_CFG_NODELIST); + hItem = (HTREEITEM) khui_cfg_get_param(node); + +#ifdef DEBUG + assert(hItem); + assert(hwtv); +#endif + + if (node == NULL) { + hw_new = d->hw_generic_pane; + } else { + khui_config_node_reg reg; + khm_int32 rv; + + hw_new = khui_cfg_get_hwnd(node); + + if (hw_new == NULL) { + rv = khui_cfg_get_reg(node, ®); +#ifdef DEBUG + assert(KHM_SUCCEEDED(rv)); +#endif + hw_new = CreateDialogParam(reg.h_module, + reg.dlg_template, + hwnd, + reg.dlg_proc, + (LPARAM) node); +#ifdef DEBUG + assert(hw_new); +#endif + khui_cfg_set_hwnd(node, hw_new); + } + } + + if (hw_new == d->hw_current) + return; /* nothing to do */ + + { + RECT r_title; + RECT r_pane; + HWND hw; + + if (d->hw_current) + ShowWindow(d->hw_current, SW_HIDE); + + hw = GetDlgItem(hwnd, IDC_CFG_TITLE); +#ifdef DEBUG + assert(hw); +#endif + GetWindowRect(hw, &r_title); + + hw = GetDlgItem(hwnd, IDC_CFG_PANE); +#ifdef DEBUG + assert(hw); +#endif + GetWindowRect(hw, &r_pane); + + OffsetRect(&r_pane, -r_title.left, -r_title.top); + + SetWindowPos(hw_new, + hwtv, + r_pane.left, r_pane.top, + r_pane.right - r_pane.left, + r_pane.bottom - r_pane.top, + SWP_NOOWNERZORDER | + SWP_SHOWWINDOW | + SWP_NOACTIVATE); + } + + if (node == NULL) { + wchar_t wbuf[256]; + + LoadString(khm_hInstance, IDS_CFG_ROOT_TITLE, + wbuf, ARRAYLENGTH(wbuf)); + + SetDlgItemText(hwnd, IDC_CFG_TITLE, wbuf); + } else { + khm_int32 rv; + khui_config_node_reg reg; + + rv = khui_cfg_get_reg(node, ®); +#ifdef DEBUG + assert(KHM_SUCCEEDED(rv)); +#endif + SetDlgItemText(hwnd, IDC_CFG_TITLE, reg.long_desc); + } + + d->hw_current = hw_new; + d->current = node; + + TreeView_SelectItem(hwtv, hItem); +} + +static BOOL +cfgui_check_mod_state(khui_config_node node) { + khm_int32 flags; + khui_config_node c = NULL; + BOOL rv = FALSE; + + flags = khui_cfg_get_flags(node); + + if (flags & KHUI_CNFLAG_MODIFIED) + return TRUE; + + if (KHM_FAILED(khui_cfg_get_first_child(node, &c))) + return FALSE; + + while(c) { + rv = (rv || cfgui_check_mod_state(c)); + khui_cfg_get_next_release(&c); + } + + return rv; +} + +static void +cfgui_apply_settings(khui_config_node node) { + HWND hwnd; + khui_config_node c; + + hwnd = khui_cfg_get_hwnd(node); + + if (hwnd) + SendMessage(hwnd, KHUI_WM_CFG_NOTIFY, + MAKEWPARAM(0, WMCFG_APPLY), + (LPARAM) node); + + if (KHM_FAILED(khui_cfg_get_first_child(node, &c))) + return; + + while (c) { + cfgui_apply_settings(c); + khui_cfg_get_next_release(&c); + } +} + +static void +cfgui_update_state(HWND hwnd, + khm_int32 flags, + khui_config_node node) { + cfgui_wnd_data * d; + HWND hwtv; + HTREEITEM hItem; + TVITEMEX itx; + int idx; + + d = cfgui_get_wnd_data(hwnd); + hwtv = GetDlgItem(hwnd, IDC_CFG_NODELIST); + hItem = (HTREEITEM) khui_cfg_get_param(node); + + ZeroMemory(&itx, sizeof(itx)); + + if (flags & KHUI_CNFLAG_MODIFIED) + idx = d->idx_modified; + else if (flags & KHUI_CNFLAG_APPLIED) + idx = d->idx_applied; + else + idx = d->idx_default; + + itx.hItem = hItem; + itx.mask = TVIF_STATE; + itx.state = INDEXTOSTATEIMAGEMASK(idx); + itx.stateMask = TVIS_STATEIMAGEMASK; + + TreeView_SetItem(hwtv, &itx); + + if(cfgui_check_mod_state(NULL)) { + EnableWindow(GetDlgItem(hwnd, IDC_CFG_SUMMARY), TRUE); + EnableWindow(GetDlgItem(hwnd, IDAPPLY), TRUE); + } else { + EnableWindow(GetDlgItem(hwnd, IDC_CFG_SUMMARY), FALSE); + EnableWindow(GetDlgItem(hwnd, IDAPPLY), FALSE); + } +} + + +/* dialog procedure for the generic dialog */ +static INT_PTR CALLBACK +cfgui_dlgproc_generic(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + cfgui_wnd_data * d; + + switch(uMsg) { + case WM_INITDIALOG: + d = (cfgui_wnd_data *) lParam; + cfgui_set_wnd_data(hwnd, d); + return TRUE; + + case WM_CTLCOLORSTATIC: + d = cfgui_get_wnd_data(hwnd); + return (BOOL)(DWORD_PTR) d->hbr_white; + + case WM_ERASEBKGND: + { + HDC hdc = (HDC) wParam; + RECT r_client; + RECT r_logo; + RECT r_fill; + + d = cfgui_get_wnd_data(hwnd); + + GetClientRect(hwnd, &r_client); + SetRectEmpty(&r_logo); + + r_logo.right = d->kbmp_logo.cx; + r_logo.bottom = d->kbmp_logo.cy; + + OffsetRect(&r_logo, + r_client.right - r_logo.right, + r_client.bottom - r_logo.bottom); + + khui_draw_bitmap(hdc, + r_logo.left, + r_logo.top, + &d->kbmp_logo); + + r_fill.left = 0; + r_fill.top = 0; + r_fill.right = r_logo.left; + r_fill.bottom = r_client.bottom; + FillRect(hdc, &r_fill, d->hbr_white); + + r_fill.left = r_logo.left; + r_fill.right = r_client.right; + r_fill.bottom = r_logo.top; + FillRect(hdc, &r_fill, d->hbr_white); + + SetWindowLong(hwnd, DWL_MSGRESULT, (LONG) TRUE); + } + return TRUE; + } + + return FALSE; +} + +static INT_PTR CALLBACK +cfgui_dlgproc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + + khui_config_node node; + cfgui_wnd_data * d; + + switch(uMsg) { + case WM_INITDIALOG: + node = (khui_config_node) lParam; + + khui_cfg_clear_params(); + + d = malloc(sizeof(*d)); + ZeroMemory(d, sizeof(*d)); + + d->hbr_white = CreateSolidBrush(RGB(255,255,255)); + + d->hw_generic_pane = + CreateDialogParam(khm_hInstance, + MAKEINTRESOURCE(IDD_CFG_GENERIC), + hwnd, + cfgui_dlgproc_generic, + (LPARAM) d); + + khui_bitmap_from_hbmp(&d->kbmp_logo, + LoadImage( + khm_hInstance, + MAKEINTRESOURCE(IDB_LOGO_OPAQUE), + IMAGE_BITMAP, + 0, + 0, + LR_DEFAULTCOLOR)); + + cfgui_set_wnd_data(hwnd, d); + + cfgui_initialize_dialog(hwnd); + + cfgui_activate_node(hwnd, node); + + khm_add_dialog(hwnd); + khm_enter_modal(hwnd); + + khui_cfg_set_configui_handle(hwnd); + + return TRUE; + + case WM_DESTROY: + cfgui_hwnd = NULL; + + khui_cfg_set_configui_handle(NULL); + + cfgui_uninitialize_dialog(hwnd); + + d = cfgui_get_wnd_data(hwnd); + khui_delete_bitmap(&d->kbmp_logo); + DeleteObject(d->hbr_white); + + khm_leave_modal(); + khm_del_dialog(hwnd); + + SetForegroundWindow(khm_hwnd_main); + + return FALSE; + + case WM_NOTIFY: + { + LPNMHDR lpnm; + LPNMTREEVIEW lptv; + + lpnm = (LPNMHDR) lParam; + + switch (lpnm->code) { + case TVN_SELCHANGED: + lptv = (LPNMTREEVIEW) lParam; + cfgui_activate_node(hwnd, + (khui_config_node) + lptv->itemNew.lParam); + return TRUE; + } + } + return TRUE; + + case WM_CTLCOLORSTATIC: + { + d = cfgui_get_wnd_data(hwnd); + return (BOOL)(DWORD_PTR) d->hbr_white; + } + /* implicit break */ + + case WM_COMMAND: + switch(wParam) { + case MAKEWPARAM(IDCANCEL, BN_CLICKED): + DestroyWindow(hwnd); + break; + + case MAKEWPARAM(IDAPPLY, BN_CLICKED): + cfgui_apply_settings(NULL); + break; + + case MAKEWPARAM(IDOK, BN_CLICKED): + cfgui_apply_settings(NULL); + DestroyWindow(hwnd); + break; + } + return TRUE; + + case KHUI_WM_CFG_NOTIFY: + switch(HIWORD(wParam)) { + case WMCFG_SHOW_NODE: + cfgui_activate_node(hwnd, (khui_config_node) lParam); + break; + + case WMCFG_UPDATE_STATE: + cfgui_update_state(hwnd, LOWORD(wParam), + (khui_config_node) lParam); + break; + } + return TRUE; + } + + return FALSE; +} + +static void +cfgui_create_window(khui_config_node node) { +#ifdef DEBUG + assert(cfgui_hwnd == NULL); +#endif + + khm_refresh_config(); + + cfgui_hwnd = CreateDialogParam(khm_hInstance, + MAKEINTRESOURCE(IDD_CFG_MAIN), + khm_hwnd_main, + cfgui_dlgproc, + (LPARAM) node); +#ifdef DEBUG + assert(cfgui_hwnd != NULL); +#endif + ShowWindow(cfgui_hwnd,SW_SHOW); +} + +static void +cfgui_destroy_window(void) { + if (cfgui_hwnd) + DestroyWindow(cfgui_hwnd); + /* cfgui_hwnd will be set to NULL in the dialog proc */ +} + +void +khm_show_config_pane(khui_config_node node) { + if (cfgui_hwnd != NULL) { + SendMessage(cfgui_hwnd, KHUI_WM_CFG_NOTIFY, + MAKEWPARAM(0, WMCFG_SHOW_NODE), + (LPARAM) node); + } else { + cfgui_create_window(node); + } +} + +void khm_refresh_config(void) { + khm_size cb; + khm_size n_idents; + wchar_t * idents = NULL; + wchar_t * t; + khm_int32 rv; + int n_tries = 0; + khui_config_node cfg_ids = NULL; + + do { + rv = kcdb_identity_enum(KCDB_IDENT_FLAG_CONFIG, + KCDB_IDENT_FLAG_CONFIG, + NULL, + &cb, + &n_idents); + + if (rv != KHM_ERROR_TOO_LONG || + n_idents == 0) + return; + + if (idents) + free(idents); + idents = malloc(cb); +#ifdef DEBUG + assert(idents); +#endif + + rv = kcdb_identity_enum(KCDB_IDENT_FLAG_CONFIG, + KCDB_IDENT_FLAG_CONFIG, + idents, + &cb, + &n_idents); + + n_tries++; + } while(KHM_FAILED(rv) && + n_tries < 5); + + if (KHM_FAILED(rv)) + goto _cleanup; + + if (KHM_FAILED(khui_cfg_open(NULL, + L"KhmIdentities", + &cfg_ids))) + goto _cleanup; + + for(t = idents; t && *t; t = multi_string_next(t)) { + khui_config_node cfg_id = NULL; + + rv = khui_cfg_open(cfg_ids, + t, + &cfg_id); + + if (KHM_FAILED(rv)) { + khui_config_node_reg reg; + wchar_t wshort[KHUI_MAXCCH_SHORT_DESC]; + wchar_t wlong[KHUI_MAXCCH_LONG_DESC]; + wchar_t wfmt[KHUI_MAXCCH_SHORT_DESC]; + + ZeroMemory(®, sizeof(reg)); + + reg.name = t; + reg.short_desc = wshort; + reg.long_desc = wlong; + reg.h_module = khm_hInstance; + reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_IDENTITY); + reg.dlg_proc = khm_cfg_identity_proc; + reg.flags = 0; + + LoadString(khm_hInstance, IDS_CFG_IDENTITY_SHORT, + wfmt, ARRAYLENGTH(wfmt)); + StringCbPrintf(wshort, sizeof(wshort), wfmt, t); + + LoadString(khm_hInstance, IDS_CFG_IDENTITY_LONG, + wfmt, ARRAYLENGTH(wfmt)); + StringCbPrintf(wlong, sizeof(wlong), wfmt, t); + + khui_cfg_register(cfg_ids, + ®); + } else { + khui_cfg_release(cfg_id); + } + } + + _cleanup: + if (cfg_ids) + khui_cfg_release(cfg_ids); + + if (idents) + free(idents); +} + +void khm_init_config(void) { + wchar_t wshort[KHUI_MAXCCH_SHORT_DESC]; + wchar_t wlong[KHUI_MAXCCH_LONG_DESC]; + khui_config_node_reg reg; + khui_config_node node; + + reg.short_desc = wshort; + reg.long_desc = wlong; + reg.h_module = khm_hInstance; + reg.flags = 0; + + reg.name = L"KhmGeneral"; + reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_GENERAL); + reg.dlg_proc = khm_cfg_general_proc; + LoadString(khm_hInstance, IDS_CFG_GENERAL_SHORT, + wshort, ARRAYLENGTH(wshort)); + LoadString(khm_hInstance, IDS_CFG_GENERAL_LONG, + wlong, ARRAYLENGTH(wlong)); + + khui_cfg_register(NULL, ®); + + reg.name = L"KhmIdentities"; + reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_IDENTITIES); + reg.dlg_proc = khm_cfg_identities_proc; + LoadString(khm_hInstance, IDS_CFG_IDENTITIES_SHORT, + wshort, ARRAYLENGTH(wshort)); + LoadString(khm_hInstance, IDS_CFG_IDENTITIES_LONG, + wlong, ARRAYLENGTH(wlong)); + + khui_cfg_register(NULL, ®); + + node = NULL; + khui_cfg_open(NULL, L"KhmIdentities", &node); +#ifdef DEBUG + assert(node); +#endif + + reg.name = L"KhmIdentitiesTab"; + reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_IDS_TAB); + reg.dlg_proc = khm_cfg_ids_tab_proc; + LoadString(khm_hInstance, IDS_CFG_IDS_TAB_SHORT, + wshort, ARRAYLENGTH(wshort)); + LoadString(khm_hInstance, IDS_CFG_IDS_TAB_LONG, + wlong, ARRAYLENGTH(wlong)); + reg.flags = KHUI_CNFLAG_SUBPANEL; + + khui_cfg_register(node, ®); + + reg.name = L"KhmIdentitiesTabPlural"; + reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_ID_TAB); + reg.dlg_proc = khm_cfg_id_tab_proc; + LoadString(khm_hInstance, IDS_CFG_ID_TAB_SHORT, + wshort, ARRAYLENGTH(wshort)); + LoadString(khm_hInstance, IDS_CFG_ID_TAB_LONG, + wlong, ARRAYLENGTH(wlong)); + reg.flags = KHUI_CNFLAG_PLURAL | KHUI_CNFLAG_SUBPANEL; + + khui_cfg_register(node, ®); + + reg.flags = 0; + khui_cfg_release(node); + + reg.name = L"KhmNotifications"; + reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_NOTIF); + reg.dlg_proc = khm_cfg_notifications_proc; + LoadString(khm_hInstance, IDS_CFG_NOTIF_SHORT, + wshort, ARRAYLENGTH(wshort)); + LoadString(khm_hInstance, IDS_CFG_NOTIF_LONG, + wlong, ARRAYLENGTH(wlong)); + + khui_cfg_register(NULL, ®); + + reg.name = L"KhmPlugins"; + reg.dlg_template = MAKEINTRESOURCE(IDD_CFG_PLUGINS); + reg.dlg_proc = khm_cfg_plugins_proc; + LoadString(khm_hInstance, IDS_CFG_PLUGINS_SHORT, + wshort, ARRAYLENGTH(wshort)); + LoadString(khm_hInstance, IDS_CFG_PLUGINS_LONG, + wlong, ARRAYLENGTH(wlong)); + + khui_cfg_register(NULL, ®); +} + +void khm_exit_config(void) { +} diff --git a/src/windows/identity/ui/configwnd.h b/src/windows/identity/ui/configwnd.h new file mode 100644 index 000000000..64da77153 --- /dev/null +++ b/src/windows/identity/ui/configwnd.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_CONFIGWND_H +#define __KHIMAIRA_CONFIGWND_H + +void +khm_show_config_pane(khui_config_node node); + +void khm_init_config(void); +void khm_exit_config(void); + +void khm_refresh_config(void); + +/* window procedures for other configuration windows */ +INT_PTR CALLBACK +khm_cfg_general_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +INT_PTR CALLBACK +khm_cfg_identities_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +INT_PTR CALLBACK +khm_cfg_identity_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +INT_PTR CALLBACK +khm_cfg_id_tab_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +INT_PTR CALLBACK +khm_cfg_ids_tab_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +INT_PTR CALLBACK +khm_cfg_notifications_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +INT_PTR CALLBACK +khm_cfg_plugins_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); +#endif diff --git a/src/windows/identity/ui/credfuncs.c b/src/windows/identity/ui/credfuncs.c new file mode 100644 index 000000000..b92e5d4a8 --- /dev/null +++ b/src/windows/identity/ui/credfuncs.c @@ -0,0 +1,827 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khmapp.h> +#include<assert.h> + +static BOOL in_dialog = FALSE; +static CRITICAL_SECTION cs_dialog; +static HANDLE in_dialog_evt = NULL; +static LONG init_dialog = 0; +static khm_int32 dialog_result = 0; + +static void +dialog_sync_init(void) { + if (InterlockedIncrement(&init_dialog) == 1) { +#ifdef DEBUG + assert(in_dialog_evt == NULL); + assert(in_dialog == FALSE); +#endif + + InitializeCriticalSection(&cs_dialog); + + in_dialog_evt = CreateEvent(NULL, + TRUE, + TRUE, + L"DialogCompletionEvent"); + } else { + InterlockedDecrement(&init_dialog); + if (in_dialog_evt == NULL) { + Sleep(100); + } + } +} + +BOOL +khm_cred_begin_dialog(void) { + BOOL rv; + + dialog_sync_init(); + + EnterCriticalSection(&cs_dialog); + + if (in_dialog) + rv = FALSE; + else { + rv = TRUE; + in_dialog = TRUE; + ResetEvent(in_dialog_evt); + } + + LeaveCriticalSection(&cs_dialog); + return rv; +} + +void +khm_cred_end_dialog(khm_int32 result) { + dialog_sync_init(); + + EnterCriticalSection(&cs_dialog); + if (in_dialog) { + in_dialog = FALSE; + SetEvent(in_dialog_evt); + } + dialog_result = result; + LeaveCriticalSection(&cs_dialog); +} + +BOOL +khm_cred_is_in_dialog(void) { + BOOL rv; + + dialog_sync_init(); + + EnterCriticalSection(&cs_dialog); + rv = in_dialog; + LeaveCriticalSection(&cs_dialog); + + return rv; +} + +khm_int32 +khm_cred_wait_for_dialog(DWORD timeout, khm_int32 * result) { + khm_int32 rv; + + dialog_sync_init(); + + EnterCriticalSection(&cs_dialog); + if (!in_dialog) + rv = KHM_ERROR_NOT_FOUND; + else { + DWORD dw; + + do { + LeaveCriticalSection(&cs_dialog); + + dw = WaitForSingleObject(in_dialog_evt, timeout); + + EnterCriticalSection(&cs_dialog); + + if (!in_dialog) { + rv = KHM_ERROR_SUCCESS; + if (result) + *result = dialog_result; + break; + } else if(dw == WAIT_TIMEOUT) { + rv = KHM_ERROR_TIMEOUT; + break; + } + } while(TRUE); + } + LeaveCriticalSection(&cs_dialog); + + return rv; +} + +/* completion handler for KMSG_CRED messages */ +void KHMAPI +kmsg_cred_completion(kmq_message *m) +{ + khui_new_creds * nc; + +#ifdef DEBUG + assert(m->type == KMSG_CRED); +#else + if(m->type != KMSG_CRED) + return; /* huh? */ +#endif + + switch(m->subtype) { + case KMSG_CRED_PASSWORD: + /* fallthrough */ + case KMSG_CRED_NEW_CREDS: + /* Cred types have attached themselves. Trigger the next + phase. */ + kmq_post_message(KMSG_CRED, KMSG_CRED_DIALOG_SETUP, 0, + m->vparam); + break; + + case KMSG_CRED_RENEW_CREDS: + nc = (khui_new_creds *) m->vparam; + + /* khm_cred_dispatch_process_message() deals with the case + where there are not credential types that wants to + participate in this operation. */ + khm_cred_dispatch_process_message(nc); + break; + + case KMSG_CRED_DIALOG_SETUP: + nc = (khui_new_creds *) m->vparam; + + khm_prep_newcredwnd(nc->hwnd); + + /* all the controls have been created. Now initialize them */ + if (nc->n_types > 0) { + kmq_post_subs_msg(nc->type_subs, + nc->n_types, + KMSG_CRED, + KMSG_CRED_DIALOG_PRESTART, + 0, + m->vparam); + } else { + PostMessage(nc->hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_DIALOG_PROCESS_COMPLETE), 0); + } + break; + + case KMSG_CRED_DIALOG_PRESTART: + /* all prestart stuff is done. Now to activate the dialog */ + nc = (khui_new_creds *) m->vparam; + khm_show_newcredwnd(nc->hwnd); + + kmq_post_subs_msg(nc->type_subs, + nc->n_types, + KMSG_CRED, + KMSG_CRED_DIALOG_START, + 0, + m->vparam); + /* at this point, the dialog window takes over. We let it run + the show until KMSG_CRED_DIALOG_END is posted by the dialog + procedure. */ + break; + + case KMSG_CRED_PROCESS: + /* a wave of these messages have completed. We should check + if there's more */ + nc = (khui_new_creds *) m->vparam; + + if(!khm_cred_dispatch_process_level(nc)) { + + if(kherr_is_error()) { + khui_alert * alert; + kherr_event * evt; + kherr_context * ctx; + wchar_t ws_title[1024]; + + ctx = kherr_peek_context(); + evt = kherr_get_err_event(ctx); + kherr_evaluate_event(evt); + + khui_alert_create_empty(&alert); + + if (nc->subtype == KMSG_CRED_PASSWORD) + LoadString(khm_hInstance, IDS_NC_PWD_FAILED_TITLE, + ws_title, ARRAYLENGTH(ws_title)); + else + LoadString(khm_hInstance, IDS_NC_FAILED_TITLE, + ws_title, ARRAYLENGTH(ws_title)); + + khui_alert_set_title(alert, ws_title); + khui_alert_set_severity(alert, evt->severity); + if(!evt->long_desc) + khui_alert_set_message(alert, evt->short_desc); + else + khui_alert_set_message(alert, evt->long_desc); + if(evt->suggestion) + khui_alert_set_suggestion(alert, evt->suggestion); + + khui_alert_show(alert); + khui_alert_release(alert); + + kherr_release_context(ctx); + + kherr_clear_error(); + } + + if (nc->subtype == KMSG_CRED_RENEW_CREDS) { + kmq_post_message(KMSG_CRED, KMSG_CRED_END, 0, + m->vparam); + } else { + PostMessage(nc->hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_DIALOG_PROCESS_COMPLETE), + 0); + } + } + break; + + case KMSG_CRED_END: + /* all is done. */ + { + khui_new_creds * nc; + + nc = (khui_new_creds *) m->vparam; + + if (nc->subtype == KMSG_CRED_NEW_CREDS || + nc->subtype == KMSG_CRED_PASSWORD) { + + if (nc->subtype == KMSG_CRED_NEW_CREDS) + khui_context_reset(); + + khm_cred_end_dialog(nc->result); + } + + khui_cw_destroy_cred_blob(nc); + + kmq_post_message(KMSG_CRED, KMSG_CRED_REFRESH, 0, 0); + + khm_cred_process_commandline(); + } + break; + + /* property sheet stuff */ + + case KMSG_CRED_PP_BEGIN: + /* all the pages should have been added by now. Just send out + the precreate message */ + kmq_post_message(KMSG_CRED, KMSG_CRED_PP_PRECREATE, 0, + m->vparam); + break; + + case KMSG_CRED_PP_END: + kmq_post_message(KMSG_CRED, KMSG_CRED_PP_DESTROY, 0, + m->vparam); + break; + + case KMSG_CRED_DESTROY_CREDS: +#ifdef DEBUG + assert(m->vparam != NULL); +#endif + khui_context_release((khui_action_context *) m->vparam); + free(m->vparam); + + kmq_post_message(KMSG_CRED, KMSG_CRED_REFRESH, 0, 0); + + khm_cred_process_commandline(); + break; + + case KMSG_CRED_IMPORT: + khm_cred_process_commandline(); + break; + } +} + +void khm_cred_import(void) +{ + _begin_task(KHERR_CF_TRANSITIVE); + _report_sr0(KHERR_NONE, IDS_CTX_IMPORT); + _describe(); + + kmq_post_message(KMSG_CRED, KMSG_CRED_IMPORT, 0, 0); + + _end_task(); +} + +void khm_cred_set_default(void) +{ + khui_action_context ctx; + khm_int32 rv; + + khui_context_get(&ctx); + + if (ctx.identity) { + rv = kcdb_identity_set_default(ctx.identity); + } + + khui_context_release(&ctx); +} + +void khm_cred_destroy_creds(void) +{ + khui_action_context * pctx; + + pctx = malloc(sizeof(*pctx)); +#ifdef DEBUG + assert(pctx); +#endif + + khui_context_get(pctx); + + if(pctx->scope == KHUI_SCOPE_NONE) { + /* this really shouldn't be necessary once we start enabling + and disbling actions based on context */ + wchar_t title[256]; + wchar_t message[256]; + + LoadString(khm_hInstance, + IDS_ALERT_NOSEL_TITLE, + title, + ARRAYLENGTH(title)); + + LoadString(khm_hInstance, + IDS_ALERT_NOSEL, + message, + ARRAYLENGTH(message)); + + khui_alert_show_simple(title, + message, + KHERR_WARNING); + + khui_context_release(pctx); + free(pctx); + } else { + _begin_task(KHERR_CF_TRANSITIVE); + _report_sr0(KHERR_NONE, IDS_CTX_RENEW_CREDS); + _describe(); + + kmq_post_message(KMSG_CRED, + KMSG_CRED_DESTROY_CREDS, + 0, + (void *) pctx); + + _end_task(); + } +} + +void khm_cred_renew_identity(khm_handle identity) +{ + khui_new_creds * c; + + khui_cw_create_cred_blob(&c); + + c->subtype = KMSG_CRED_RENEW_CREDS; + c->result = KHUI_NC_RESULT_GET_CREDS; + khui_context_create(&c->ctx, + KHUI_SCOPE_IDENT, + identity, + KCDB_CREDTYPE_INVALID, + NULL); + + _begin_task(KHERR_CF_TRANSITIVE); + _report_sr0(KHERR_NONE, IDS_CTX_RENEW_CREDS); + _describe(); + + kmq_post_message(KMSG_CRED, KMSG_CRED_RENEW_CREDS, 0, (void *) c); + + _end_task(); +} + +void khm_cred_renew_cred(khm_handle cred) +{ + khui_new_creds * c; + + khui_cw_create_cred_blob(&c); + + c->subtype = KMSG_CRED_RENEW_CREDS; + c->result = KHUI_NC_RESULT_GET_CREDS; + khui_context_create(&c->ctx, + KHUI_SCOPE_CRED, + NULL, + KCDB_CREDTYPE_INVALID, + cred); + + _begin_task(KHERR_CF_TRANSITIVE); + _report_sr0(KHERR_NONE, IDS_CTX_RENEW_CREDS); + _describe(); + + kmq_post_message(KMSG_CRED, KMSG_CRED_RENEW_CREDS, 0, (void *) c); + + _end_task(); +} + +void khm_cred_renew_creds(void) +{ + khui_new_creds * c; + + khui_cw_create_cred_blob(&c); + c->subtype = KMSG_CRED_RENEW_CREDS; + c->result = KHUI_NC_RESULT_GET_CREDS; + khui_context_get(&c->ctx); + + _begin_task(KHERR_CF_TRANSITIVE); + _report_sr0(KHERR_NONE, IDS_CTX_RENEW_CREDS); + _describe(); + + kmq_post_message(KMSG_CRED, KMSG_CRED_RENEW_CREDS, 0, (void *) c); + + _end_task(); +} + +void khm_cred_change_password(wchar_t * title) +{ + khui_new_creds * nc; + LPNETID_DLGINFO pdlginfo; + khm_size cb; + + if (!khm_cred_begin_dialog()) + return; + + khui_cw_create_cred_blob(&nc); + nc->subtype = KMSG_CRED_PASSWORD; + + khui_context_get(&nc->ctx); + + kcdb_identpro_get_ui_cb((void *) &nc->ident_cb); + + assert(nc->ident_cb); + + if (title) { + + if (SUCCEEDED(StringCbLength(title, KHUI_MAXCB_TITLE, &cb))) { + cb += sizeof(wchar_t); + + nc->window_title = malloc(cb); +#ifdef DEBUG + assert(nc->window_title); +#endif + StringCbCopy(nc->window_title, cb, title); + } + } else if (nc->ctx.cb_vparam == sizeof(NETID_DLGINFO) && + (pdlginfo = nc->ctx.vparam) && + pdlginfo->size == NETID_DLGINFO_V1_SZ && + pdlginfo->in.title[0] && + SUCCEEDED(StringCchLength(pdlginfo->in.title, + NETID_TITLE_SZ, + &cb))) { + + cb = (cb + 1) * sizeof(wchar_t); + nc->window_title = malloc(cb); +#ifdef DEBUG + assert(nc->window_title); +#endif + StringCbCopy(nc->window_title, cb, pdlginfo->in.title); + } + + khm_create_newcredwnd(khm_hwnd_main, nc); + + if (nc->hwnd != NULL) { + _begin_task(KHERR_CF_TRANSITIVE); + _report_sr0(KHERR_NONE, IDS_CTX_PASSWORD); + _describe(); + + kmq_post_message(KMSG_CRED, KMSG_CRED_PASSWORD, 0, + (void *) nc); + + _end_task(); + } else { + khui_cw_destroy_cred_blob(nc); + } +} + +void khm_cred_obtain_new_creds(wchar_t * title) +{ + khui_new_creds * nc; + LPNETID_DLGINFO pdlginfo; + khm_size cb; + + if (!khm_cred_begin_dialog()) + return; + + khui_cw_create_cred_blob(&nc); + nc->subtype = KMSG_CRED_NEW_CREDS; + + khui_context_get(&nc->ctx); + + kcdb_identpro_get_ui_cb((void *) &nc->ident_cb); + + assert(nc->ident_cb); + + if (title) { + if (SUCCEEDED(StringCbLength(title, KHUI_MAXCB_TITLE, &cb))) { + cb += sizeof(wchar_t); + + nc->window_title = malloc(cb); +#ifdef DEBUG + assert(nc->window_title); +#endif + StringCbCopy(nc->window_title, cb, title); + } + } else if (nc->ctx.cb_vparam == sizeof(NETID_DLGINFO) && + (pdlginfo = nc->ctx.vparam) && + pdlginfo->size == NETID_DLGINFO_V1_SZ && + pdlginfo->in.title[0] && + SUCCEEDED(StringCchLength(pdlginfo->in.title, + NETID_TITLE_SZ, + &cb))) { + + cb = (cb + 1) * sizeof(wchar_t); + nc->window_title = malloc(cb); +#ifdef DEBUG + assert(nc->window_title); +#endif + StringCbCopy(nc->window_title, cb, pdlginfo->in.title); + } + + khm_create_newcredwnd(khm_hwnd_main, nc); + + if (nc->hwnd != NULL) { + _begin_task(KHERR_CF_TRANSITIVE); + _report_sr0(KHERR_NONE, IDS_CTX_NEW_CREDS); + _describe(); + + kmq_post_message(KMSG_CRED, KMSG_CRED_NEW_CREDS, 0, + (void *) nc); + + _end_task(); + } else { + khui_cw_destroy_cred_blob(nc); + } +} + +/* this is called by khm_cred_dispatch_process_message and the + kmsg_cred_completion to initiate and continue checked broadcasts of + KMSG_CRED_DIALOG_PROCESS messages. + + Returns TRUE if more KMSG_CRED_DIALOG_PROCESS messages were + posted. */ +BOOL khm_cred_dispatch_process_level(khui_new_creds *nc) +{ + khm_size i,j; + khm_handle subs[KHUI_MAX_NCTYPES]; + int n_subs = 0; + BOOL cont = FALSE; + khui_new_creds_by_type *t, *d; + + /* at each level, we dispatch a wave of notifications to plug-ins + who's dependencies are all satisfied */ + EnterCriticalSection(&nc->cs); + + /* if any types have already completed, we mark them are processed + and skip them */ + for (i=0; i < nc->n_types; i++) { + t = nc->types[i]; + if(t->flags & KHUI_NC_RESPONSE_COMPLETED) + t->flags |= KHUI_NCT_FLAG_PROCESSED; + } + + for(i=0; i<nc->n_types; i++) { + t = nc->types[i]; + + if((t->flags & KHUI_NCT_FLAG_PROCESSED) || + (t->flags & KHUI_NC_RESPONSE_COMPLETED)) + continue; + + for(j=0; j<t->n_type_deps; j++) { + if(KHM_FAILED(khui_cw_find_type(nc, t->type_deps[j], &d))) + break; + + if(!(d->flags & KHUI_NC_RESPONSE_COMPLETED)) + break; + } + + if(j<t->n_type_deps) /* there are unmet dependencies */ + continue; + + /* all dependencies for this type have been met. */ + subs[n_subs++] = kcdb_credtype_get_sub(t->type); + t->flags |= KHUI_NCT_FLAG_PROCESSED; + cont = TRUE; + } + + LeaveCriticalSection(&nc->cs); + + /* the reason why we are posting messages in batches is because + when the message has completed we know that all the types that + have the KHUI_NCT_FLAG_PROCESSED set have completed processing. + Otherwise we have to individually track each message and update + the type */ + if(n_subs > 0) + kmq_post_subs_msg(subs, n_subs, KMSG_CRED, KMSG_CRED_PROCESS, 0, + (void *) nc); + + return cont; +} + +void +khm_cred_dispatch_process_message(khui_new_creds *nc) +{ + khm_size i; + BOOL pending; + wchar_t wsinsert[512]; + khm_size cbsize; + + /* see if there's anything to do. We can check this without + obtaining a lock */ + if(nc->n_types == 0 || + (nc->subtype == KMSG_CRED_NEW_CREDS && + nc->n_identities == 0) || + (nc->subtype == KMSG_CRED_PASSWORD && + nc->n_identities == 0)) + goto _terminate_job; + + /* check dependencies and stuff first */ + EnterCriticalSection(&nc->cs); + for(i=0; i<nc->n_types; i++) { + nc->types[i]->flags &= ~ KHUI_NCT_FLAG_PROCESSED; + } + LeaveCriticalSection(&nc->cs); + + /* Consindering all that can go wrong here and the desire to + handle errors here separately from others, we create a new task + for the purpose of tracking the credentials acquisition + process. */ + _begin_task(KHERR_CF_TRANSITIVE); + + /* Describe the context */ + if(nc->subtype == KMSG_CRED_NEW_CREDS) { + cbsize = sizeof(wsinsert); + kcdb_identity_get_name(nc->identities[0], wsinsert, &cbsize); + + _report_sr1(KHERR_NONE, IDS_CTX_PROC_NEW_CREDS, + _cstr(wsinsert)); + _resolve(); + } else if (nc->subtype == KMSG_CRED_RENEW_CREDS) { + cbsize = sizeof(wsinsert); + + if (nc->ctx.scope == KHUI_SCOPE_IDENT) + kcdb_identity_get_name(nc->ctx.identity, wsinsert, &cbsize); + else if (nc->ctx.scope == KHUI_SCOPE_CREDTYPE) { + if (nc->ctx.identity != NULL) + kcdb_identity_get_name(nc->ctx.identity, wsinsert, + &cbsize); + else + kcdb_credtype_get_name(nc->ctx.cred_type, wsinsert, + &cbsize); + } else if (nc->ctx.scope == KHUI_SCOPE_CRED) { + kcdb_cred_get_name(nc->ctx.cred, wsinsert, &cbsize); + } else { + StringCbCopy(wsinsert, sizeof(wsinsert), L"(?)"); + } + + _report_sr1(KHERR_NONE, IDS_CTX_PROC_RENEW_CREDS, + _cstr(wsinsert)); + _resolve(); + } else if (nc->subtype == KMSG_CRED_PASSWORD) { + cbsize = sizeof(wsinsert); + kcdb_identity_get_name(nc->identities[0], wsinsert, &cbsize); + + _report_sr1(KHERR_NONE, IDS_CTX_PROC_PASSWORD, + _cstr(wsinsert)); + _resolve(); + } else { + assert(FALSE); + } + + _describe(); + + pending = khm_cred_dispatch_process_level(nc); + + _end_task(); + + if(!pending) + goto _terminate_job; + + return; + + _terminate_job: + if (nc->subtype == KMSG_CRED_RENEW_CREDS) + kmq_post_message(KMSG_CRED, KMSG_CRED_END, 0, (void *) nc); + else + PostMessage(nc->hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_DIALOG_PROCESS_COMPLETE), 0); +} + +void +khm_cred_process_commandline(void) { + khm_handle defident = NULL; + + if (!khm_startup.processing) + return; + + if (khm_startup.init || + khm_startup.renew || + khm_startup.destroy) { + kcdb_identity_get_default(&defident); + } + + do { + if (khm_startup.init) { + if (defident) + khui_context_set(KHUI_SCOPE_IDENT, + defident, + KCDB_CREDTYPE_INVALID, + NULL, NULL, 0, + NULL); + else + khui_context_reset(); + + khm_cred_obtain_new_creds(NULL); + khm_startup.init = FALSE; + break; + } + + if (khm_startup.import) { + khm_cred_import(); + khm_startup.import = FALSE; + break; + } + + if (khm_startup.renew) { + if (defident) + khui_context_set(KHUI_SCOPE_IDENT, + defident, + KCDB_CREDTYPE_INVALID, + NULL, NULL, 0, + NULL); + else + khui_context_reset(); + + khm_cred_renew_creds(); + khm_startup.renew = FALSE; + break; + } + + if (khm_startup.destroy) { + if (defident) { + khui_context_set(KHUI_SCOPE_IDENT, + defident, + KCDB_CREDTYPE_INVALID, + NULL, NULL, 0, + NULL); + + khm_cred_destroy_creds(); + } + + khm_startup.destroy = FALSE; + break; + } + + if (khm_startup.autoinit) { + khm_size count; + + kcdb_credset_get_size(NULL, &count); + + if (count == 0) { + khm_cred_obtain_new_creds(NULL); + } + khm_startup.autoinit = FALSE; + break; + } + + if (khm_startup.exit) { + PostMessage(khm_hwnd_main, + WM_COMMAND, + MAKEWPARAM(KHUI_ACTION_EXIT, 0), 0); + khm_startup.exit = FALSE; + break; + } + + khm_startup.processing = FALSE; + } while(FALSE); + + if (defident) + kcdb_identity_release(defident); +} + +void +khm_cred_begin_commandline(void) { + if (khm_startup.seen) + return; + + khm_startup.seen = TRUE; + khm_startup.processing = TRUE; + + khm_cred_process_commandline(); +} diff --git a/src/windows/identity/ui/credfuncs.h b/src/windows/identity/ui/credfuncs.h new file mode 100644 index 000000000..b25b6630e --- /dev/null +++ b/src/windows/identity/ui/credfuncs.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_CREDFUNCS_H +#define __KHIMAIRA_CREDFUNCS_H + +void KHMAPI +kmsg_cred_completion(kmq_message *m); + +void +khm_cred_destroy_creds(void); + +void +khm_cred_renew_identity(khm_handle identity); + +void +khm_cred_renew_cred(khm_handle cred); + +void +khm_cred_renew_creds(void); + +void +khm_cred_obtain_new_creds(wchar_t * window_title); + +void +khm_cred_set_default(void); + +void +khm_cred_change_password(wchar_t * window_title); + +void +khm_cred_dispatch_process_message(khui_new_creds *nc); + +BOOL +khm_cred_dispatch_process_level(khui_new_creds *nc); + +BOOL +khm_cred_is_in_dialog(void); + +khm_int32 +khm_cred_wait_for_dialog(DWORD timeout, khm_int32 * result); + +void +khm_cred_begin_commandline(void); + +void +khm_cred_process_commandline(void); + +#endif diff --git a/src/windows/identity/ui/credwnd.c b/src/windows/identity/ui/credwnd.c new file mode 100644 index 000000000..784a7f90b --- /dev/null +++ b/src/windows/identity/ui/credwnd.c @@ -0,0 +1,3223 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khmapp.h> +#include<prsht.h> +#include<assert.h> + +ATOM khui_credwnd_cls; +khm_int32 khui_cw_flag_id; + +khm_int32 +cw_get_custom_attr_id(wchar_t * s) +{ + if(!wcscmp(s, CW_CANAME_FLAGS)) + return CW_CA_FLAGS; + if(!wcscmp(s, CW_CANAME_TYPEICON)) + return CW_CA_TYPEICON; + return 0; +} + +void +cw_load_view(khui_credwnd_tbl * tbl, wchar_t * view, HWND hwnd) { + khm_handle hc_cw = NULL; + khm_handle hc_vs = NULL; + khm_handle hc_v = NULL; + khm_handle hc_cs = NULL; + khm_handle hc_c = NULL; + wchar_t buf[KCONF_MAXCCH_NAME]; + wchar_t * clist = NULL; + khm_size cbsize; + wchar_t * cstr = NULL; + wchar_t * iter = NULL; + int i; + HDC hdc; + + tbl->hwnd = hwnd; + + if(KHM_FAILED(khc_open_space(NULL, L"CredWindow", KHM_PERM_READ, &hc_cw))) + return; + if(KHM_FAILED(khc_open_space(hc_cw, L"Views", KHM_PERM_READ, &hc_vs))) + goto _exit; + + if(!view) { + cbsize = sizeof(buf); + if(KHM_FAILED(khc_read_string(hc_cw, L"DefaultView", buf, &cbsize))) + goto _exit; + view = buf; + } + + if(KHM_FAILED(khc_open_space(hc_vs, view, KHM_PERM_READ, &hc_v))) + goto _exit; + + if(KHM_FAILED(khc_open_space(hc_v, L"Columns", KHM_PERM_READ, &hc_cs))) + goto _exit; + + cbsize = 0; + if(khc_read_multi_string(hc_v, L"ColumnList", NULL, &cbsize) != KHM_ERROR_TOO_LONG) + goto _exit; + + clist = malloc(cbsize); + + if(KHM_FAILED(khc_read_multi_string(hc_v, L"ColumnList", clist, &cbsize))) + goto _exit; + + tbl->n_cols = (int) multi_string_length_n(clist); + tbl->n_total_cols = UBOUNDSS(tbl->n_cols, KHUI_CW_COL_INITIAL, KHUI_CW_COL_INCREMENT); + tbl->cols = malloc(sizeof(khui_credwnd_col) * tbl->n_total_cols); + ZeroMemory(tbl->cols, sizeof(khui_credwnd_col) * tbl->n_total_cols); + + iter = clist; + i = 0; + while(iter) { + khm_int32 attr_id; + + attr_id = cw_get_custom_attr_id(iter); + if(!attr_id) { + /* a KCDB attribute */ + if(KHM_FAILED(kcdb_attrib_get_id(iter, &attr_id))) + goto _skip_col; + if(kcdb_attrib_describe(attr_id, NULL, &cbsize, KCDB_TS_SHORT) != KHM_ERROR_TOO_LONG || + cbsize == 0) + goto _skip_col; + tbl->cols[i].title = malloc(cbsize); + kcdb_attrib_describe(attr_id, tbl->cols[i].title, &cbsize, KCDB_TS_SHORT); + } else { + /* All current custom attributes are represented by icons, + not names */ + tbl->cols[i].title = NULL; + } + + tbl->cols[i].attr_id = attr_id; + + if(KHM_SUCCEEDED(khc_open_space(hc_cs, iter, KHM_PERM_READ, &hc_c))) { + if(KHM_FAILED(khc_read_int32(hc_c, L"Flags", &(tbl->cols[i].flags)))) + tbl->cols[i].flags = 0; + if(KHM_FAILED(khc_read_int32(hc_c, L"Width", &(tbl->cols[i].width)))) + tbl->cols[i].width = -1; + if(KHM_FAILED(khc_read_int32(hc_c, L"SortIndex", &(tbl->cols[i].sort_index)))) + tbl->cols[i].sort_index = -1; + khc_close_space(hc_c); + hc_c = NULL; + } else { + tbl->cols[i].flags = 0; + tbl->cols[i].width = -1; + tbl->cols[i].sort_index = -1; + } + i++; +_skip_col: + iter = multi_string_next(iter); + } + + /* adjust the number of columns. We may have skipped columns due to + inconsistencies above */ + tbl->n_cols = i; + + /* now that all the columns have been loaded, load the view + parameters */ + if(KHM_FAILED(khc_read_int32(hc_v, L"PaddingHorizontal", &(tbl->hpad)))) + khc_read_int32(hc_cw, L"PaddingHorizontal", &(tbl->hpad)); + if(KHM_FAILED(khc_read_int32(hc_v, L"PaddingVertical", &(tbl->vpad)))) + khc_read_int32(hc_cw, L"PaddingVertical", &(tbl->vpad)); + if(KHM_FAILED(khc_read_int32(hc_v, L"PaddingHeader", &(tbl->hpad_h)))) + khc_read_int32(hc_cw, L"PaddingHeader", &(tbl->hpad_h)); + if(KHM_FAILED(khc_read_int32(hc_v, L"WarnThreshold", &(tbl->threshold_warn)))) + khc_read_int32(hc_cw, L"WarnThreshold", &(tbl->threshold_warn)); + if(KHM_FAILED(khc_read_int32(hc_v, L"CriticalThreshold", &(tbl->threshold_critical)))) + khc_read_int32(hc_cw, L"CriticalThreshold", &(tbl->threshold_critical)); + + /* and the font resources and stuff */ + + tbl->flags |= KHUI_CW_TBL_INITIALIZED | KHUI_CW_TBL_COL_DIRTY | KHUI_CW_TBL_ACTIVE; + + /*TODO: the graphics objects should be customizable */ + + hdc = GetWindowDC(hwnd); + + tbl->hf_header = CreateFont( + -MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72),0, /* width/height */ + 0,0, /* escapement */ + FW_THIN, + FALSE, + FALSE, + FALSE, + DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, + DEFAULT_QUALITY, + FF_SWISS, + L"MS Shell Dlg"); + + if(tbl->hf_header && tbl->hwnd_header) + SendMessage(tbl->hwnd_header, WM_SETFONT, (WPARAM) tbl->hf_header, 0); + + tbl->hf_bold_header = CreateFont( + -MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72),0, /* width/height */ + 0,0, /* escapement */ + FW_BOLD, + FALSE, + FALSE, + FALSE, + DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, + DEFAULT_QUALITY, + FF_SWISS, + L"MS Shell Dlg"); + + tbl->hf_normal = CreateFont( + -MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72),0, /* width/height */ + 0,0, /* escapement */ + FW_THIN, + FALSE, + FALSE, + FALSE, + DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, + DEFAULT_QUALITY, + FF_SWISS, + L"MS Shell Dlg"); + + tbl->hf_bold = CreateFont( + -MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72),0, /* width/height */ + 0,0, /* escapement */ + FW_BOLD, + FALSE, + FALSE, + FALSE, + DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, + DEFAULT_QUALITY, + FF_SWISS, + L"MS Shell Dlg"); + + ReleaseDC(hwnd, hdc); + + khui_bitmap_from_hbmp(&(tbl->kbm_logo_shade),LoadImage( + khm_hInstance, + MAKEINTRESOURCE(IDB_LOGO_SHADE), + IMAGE_BITMAP, + 0, + 0, + LR_DEFAULTCOLOR)); + + tbl->hb_normal = CreateSolidBrush(RGB(255,255,255)); + tbl->hb_grey = CreateSolidBrush(RGB(240,240,240)); + tbl->hb_sel = CreateSolidBrush(RGB(230,230,255)); + tbl->hb_hdr_bg = CreateSolidBrush(RGB(230,230,230)); + tbl->hb_hdr_bg_sel = CreateSolidBrush(RGB(0,0,255)); + tbl->hb_hdr_bg_crit = CreateSolidBrush(RGB(240,133,117)); + tbl->hb_hdr_bg_warn = CreateSolidBrush(RGB(251,199,77)); + tbl->hb_hdr_bg_exp = CreateSolidBrush(RGB(255,144,144)); + + tbl->cr_normal = RGB(0,0,0); + tbl->cr_sel = RGB(0,0,0); + tbl->cr_hdr_outline = RGB(0,0,0); + tbl->cr_hdr_normal = RGB(0,0,0); + tbl->cr_hdr_sel = RGB(255,255,255); + + tbl->ilist = khui_create_ilist(KHUI_SMICON_CX, KHUI_SMICON_CY-1, 16, 8, 0); + { + HBITMAP hbm; + +#define ADD_BITMAP(i) \ + hbm = LoadImage(khm_hInstance, MAKEINTRESOURCE(i), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR); \ + if(hbm) { \ + khui_ilist_add_masked_id(tbl->ilist, hbm, KHUI_TOOLBAR_BGCOLOR, i); \ + DeleteObject(hbm); \ + } + + ADD_BITMAP(IDB_WDG_COLLAPSE); + ADD_BITMAP(IDB_WDG_EXPAND); + ADD_BITMAP(IDB_ID_SM); + ADD_BITMAP(IDB_ID_DIS_SM); + ADD_BITMAP(IDB_TK_NEW_SM); + ADD_BITMAP(IDB_TK_REFRESH_SM); + ADD_BITMAP(IDB_WDG_COLLAPSE_HI); + ADD_BITMAP(IDB_WDG_EXPAND_HI); + ADD_BITMAP(IDB_WDG_FLAG); + ADD_BITMAP(IDB_WDG_CREDTYPE); + ADD_BITMAP(IDB_FLAG_WARN); + ADD_BITMAP(IDB_FLAG_EXPIRED); + ADD_BITMAP(IDB_FLAG_CRITICAL); + +#undef ADD_BITMAP + } + + tbl->cursor_row = -1; + tbl->scr_left = 0; + tbl->scr_top = 0; + tbl->ext_height = 0; + tbl->ext_width = 0; + +_exit: + if(hc_cw) + khc_close_space(hc_cw); + if(hc_vs) + khc_close_space(hc_vs); + if(hc_v) + khc_close_space(hc_v); + if(hc_cs) + khc_close_space(hc_cs); + if(clist) + free(clist); +} + +void +cw_update_creds(khui_credwnd_tbl * tbl) +{ + kcdb_cred_comp_field * fields; + kcdb_cred_comp_order comp_order; + khm_size i; + khm_int32 n; + khm_int32 delta; + khm_handle hc; + khm_int32 flags; + + if(!tbl->credset) { + if(KHM_FAILED(kcdb_credset_create(&(tbl->credset)))) + return; + } + + kcdb_credset_purge(tbl->credset); + + kcdb_identity_refresh_all(); + + kcdb_credset_collect( + tbl->credset, + NULL, + NULL, + KCDB_CREDTYPE_ALL, + &delta); + + /* now we need to figure out how to sort the credentials */ + fields = malloc(sizeof(kcdb_cred_comp_field) * tbl->n_cols); + ZeroMemory(fields, sizeof(kcdb_cred_comp_field) * tbl->n_cols); + + for(i=0, n=0; i<tbl->n_cols; i++) { + if((tbl->cols[i].flags & KHUI_CW_COL_SORT_INC) || + (tbl->cols[i].flags & KHUI_CW_COL_SORT_DEC) || + (tbl->cols[i].flags & KHUI_CW_COL_GROUP)) + { + int si; + /* we need to sort by this column */ + si = tbl->cols[i].sort_index; + + if(si < 0 || si >= (int) tbl->n_cols) + { + /* this shouldn't happen */ + tbl->cols[i].flags &= ~(KHUI_CW_COL_SORT_INC | + KHUI_CW_COL_SORT_DEC | + KHUI_CW_COL_GROUP); + continue; + } + + fields[si].attrib = tbl->cols[i].attr_id; + if(tbl->cols[i].flags & KHUI_CW_COL_SORT_DEC) + fields[si].order = KCDB_CRED_COMP_DECREASING; + else + fields[si].order = KCDB_CRED_COMP_INCREASING; + + /* special case. if we are sorting by name, we group + initial tickets before non-initial tickets. + + Also, if we are sorting by credential type name, then + we allow the primary credential type first before + others. + */ + + if (fields[si].attrib == KCDB_ATTR_NAME || + fields[si].attrib == KCDB_ATTR_TYPE_NAME) + fields[si].order |= KCDB_CRED_COMP_INITIAL_FIRST; + + if(si >= n) + n = si+1; + } + } + + /* we assume that the sort order is sane */ + /*TODO: don't assume; check if the sort order is sane */ + + comp_order.nFields = n; + comp_order.fields = fields; + + kcdb_credset_sort(tbl->credset, + kcdb_cred_comp_generic, + (void *) &comp_order); + + /* also, if new credentials were added, initialize the UI flag + attribute to 0 */ + if(delta & KCDB_DELTA_ADD) { + khm_size s; + + kcdb_credset_get_size(tbl->credset, &s); + for(i=0;i<s;i++) { + if(KHM_FAILED(kcdb_credset_get_cred(tbl->credset, (khm_int32) i, &hc))) + continue; /* lost a race */ + if(KHM_FAILED(kcdb_cred_get_attr(hc, khui_cw_flag_id, NULL, NULL, NULL))) { + flags = 0; + kcdb_cred_set_attr(hc, khui_cw_flag_id, &flags, sizeof(flags)); + } + kcdb_cred_release(hc); + } + } +} + +void +cw_del_outline(khui_credwnd_outline *o) { + khui_credwnd_outline * c; + if(!o) + return; + + /* the outline object is still in a list */ + if(o->next || o->prev) + return; + + if(o->header) + free(o->header); + if ((o->flags & KHUI_CW_O_DATAALLOC) && + o->data) + free(o->data); + + LPOP(&(o->children), &c); + while(c) { + cw_del_outline(c); + LPOP(&(o->children), &c); + } + + free(o); +} + +khui_credwnd_outline * +cw_new_outline_node(wchar_t * heading) { + khui_credwnd_outline * o; + size_t cblen; + + o = malloc(sizeof(khui_credwnd_outline)); + ZeroMemory(o, sizeof(khui_credwnd_outline)); + + if(SUCCEEDED(StringCbLength(heading, KHUI_MAXCB_HEADING, &cblen))) { + cblen += sizeof(wchar_t); + o->header = malloc(cblen); + StringCbCopy(o->header, cblen, heading); + } + + return o; +} + +khm_int32 +cw_get_cred_exp_flags(khui_credwnd_tbl * tbl, khm_handle cred) +{ + khm_int32 flags; + long s; + FILETIME ft; + khm_size cbsize; + + cbsize = sizeof(ft); + if(KHM_FAILED(kcdb_cred_get_attr(cred, KCDB_ATTR_TIMELEFT, NULL, &ft, &cbsize))) + return 0; + + s = FtIntervalToMilliseconds(&ft) / 1000; + + flags = 0; + if(s < 0) + flags = CW_EXPSTATE_EXPIRED; + else if(s < tbl->threshold_critical) + flags = CW_EXPSTATE_CRITICAL; + else if(s < tbl->threshold_warn) + flags = CW_EXPSTATE_WARN; + else + flags = CW_EXPSTATE_NONE; + + return flags; +} + +void cw_update_outline(khui_credwnd_tbl * tbl); + +VOID CALLBACK +cw_timer_proc(HWND hwnd, + UINT uMsg, + UINT_PTR idEvent, + DWORD dwTime) +{ + khui_credwnd_tbl * tbl; + khui_credwnd_row * r; + khm_int32 nflags; + khm_size nr; + long ms; + FILETIME ft; + khm_size cbsize; + + tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + r = (khui_credwnd_row *) idEvent; + + nr = r - tbl->rows; + + if(nr < 0 || nr >= tbl->n_rows) + return; + + if(!(r->flags & KHUI_CW_ROW_CRED)) + return; /* we only know what to do with cred rows */ + + nflags = cw_get_cred_exp_flags(tbl, (khm_handle) r->data); + if((r->flags & CW_EXPSTATE_MASK) != nflags) { + /* flags have changed */ + /* the outline needs to be updated */ + cw_update_outline(tbl); + InvalidateRect(tbl->hwnd, NULL, FALSE); + } else { + /* just invalidate the row */ + RECT r,rr,ri; + + GetClientRect(tbl->hwnd, &r); + r.top += tbl->header_height; + rr.top = r.top + (long)nr * tbl->cell_height - tbl->scr_top; + rr.bottom = rr.top + tbl->cell_height; + rr.left = r.left; + rr.right = r.right; + + if(IntersectRect(&ri, &r, &rr)) + InvalidateRect(tbl->hwnd, &ri, FALSE); + } + + cbsize = sizeof(ft); + if(KHM_SUCCEEDED(kcdb_cred_get_attr((khm_handle) r->data, KCDB_ATTR_TIMELEFT, NULL, &ft, &cbsize))) + { + ms = FtIntervalMsToRepChange(&ft); + if(ms > 0) { + SetTimer(tbl->hwnd, (UINT_PTR) r, ms + 100, cw_timer_proc); + } + } +} + +void +cw_set_tbl_row_cred(khui_credwnd_tbl * tbl, + int row, + khm_handle cred, + int col) +{ + FILETIME ft; + long ms; + khm_size cbsize; + + if((int) tbl->n_total_rows <= row) { + /* we need to resize the allocation */ + khui_credwnd_row * newrows; + khm_size newsize; + + newsize = UBOUNDSS(row+1,KHUI_CW_ROW_INITIAL, KHUI_CW_ROW_INCREMENT); + newrows = malloc(sizeof(khui_credwnd_row) * newsize); + memcpy(newrows, tbl->rows, sizeof(khui_credwnd_row) * tbl->n_rows); + free(tbl->rows); + tbl->rows = newrows; + tbl->n_total_rows = newsize; + } + + tbl->rows[row].col = col; + tbl->rows[row].data = cred; + tbl->rows[row].flags = KHUI_CW_ROW_CRED; + + /* Set any required timer events */ + cbsize = sizeof(ft); + if(KHM_SUCCEEDED(kcdb_cred_get_attr(cred, KCDB_ATTR_TIMELEFT, NULL, &ft, &cbsize))) { + ms = FtIntervalMsToRepChange(&ft); + if(ms > 0) { + SetTimer(tbl->hwnd, (UINT_PTR) &(tbl->rows[row]), ms + 100, cw_timer_proc); + tbl->rows[row].flags |= KHUI_CW_ROW_TIMERSET; + } + } +} + +void +cw_set_tbl_row_header(khui_credwnd_tbl * tbl, + int row, int col, + khui_credwnd_outline * o) +{ + if((int) tbl->n_total_rows <= row) { + /* we need to resize the allocation */ + khui_credwnd_row * newrows; + khm_size newsize; + + newsize = UBOUNDSS(row+1,KHUI_CW_ROW_INITIAL, KHUI_CW_ROW_INCREMENT); + newrows = malloc(sizeof(khui_credwnd_row) * newsize); + memcpy(newrows, tbl->rows, sizeof(khui_credwnd_row) * tbl->n_rows); + free(tbl->rows); + tbl->rows = newrows; + tbl->n_total_rows = newsize; + } + + tbl->rows[row].col = col; + tbl->rows[row].data = (khm_handle) o; + tbl->rows[row].flags = KHUI_CW_ROW_HEADER; + if(o->flags & KHUI_CW_O_SELECTED) + tbl->rows[row].flags |= KHUI_CW_ROW_SELECTED; +} + +static int +iwcscmp(const void * p1, const void * p2) { + const wchar_t * s1 = *(wchar_t **) p1; + const wchar_t * s2 = *(wchar_t **) p2; + + return wcscmp(s1, s2); +} + +void +cw_update_outline(khui_credwnd_tbl * tbl) +{ + int i,j,n_rows; + int level; + int visible; + khm_size n_creds; + khm_handle prevcred = NULL; + khm_handle thiscred = NULL; + /* grouping[0..n_grouping-1] are the columns that we are going to + group the display by. Say we are grouping by identity and then + by type, then grouping[0]=col# of identity and grouping[1]=col# + of type */ + khm_int32 * grouping = NULL; + khui_credwnd_outline * ol = NULL; + int n_grouping; + wchar_t buf[256]; + khm_size cbbuf; + khm_int32 flags; + int selected; + + /* this is called after calling cw_update_creds, so we assume + that the credentials are all loaded and sorted according to + grouping rules */ + + /* if the columns have changed, then any outline info we have + cached are unreliable */ + if(tbl->flags & KHUI_CW_TBL_COL_DIRTY) { + khui_credwnd_outline * o; + LPOP(&(tbl->outline), &o); + while(o) { + cw_del_outline(o); + LPOP(&(tbl->outline), &o); + } + tbl->n_rows = 0; + } + + /* Otherwise, we should reset the outline indices. Just the first + level is enough */ + if (tbl->outline) { + khui_credwnd_outline * o; + + o = tbl->outline; + while(o) { + o->start = -1; + o = LNEXT(o); + } + } + + + /* determine the grouping order */ + grouping = malloc(sizeof(khm_int32) * tbl->n_cols); + for(i=0; i < (int) tbl->n_cols; i++) + grouping[i] = -1; + n_grouping = 0; + + for(i=0; i < (int) tbl->n_cols; i++) { + /* since cw_update_creds has run, the KHUI_CW_COL_GROUP flag + only exists for columns that has a valid sort_index */ + if(tbl->cols[i].flags & KHUI_CW_COL_GROUP) { + grouping[tbl->cols[i].sort_index] = i; + if(n_grouping <= tbl->cols[i].sort_index) + n_grouping = tbl->cols[i].sort_index + 1; + } + } + + /* if we have sorted by an index without grouping by it, we can't + establish any grouping beyond that index. */ + for(i=0; i < n_grouping; i++) { + if(grouping[i] == -1) + break; + } + n_grouping = i; + + if(!tbl->rows) { + /* we haven't allocated memory yet */ + tbl->n_total_rows = KHUI_CW_ROW_INITIAL; + tbl->n_rows = 0; + tbl->rows = malloc(sizeof(khui_credwnd_row) * tbl->n_total_rows); + } else { + /* kill any pending timers */ + for(i=0; i < (int) tbl->n_rows; i++) + if(tbl->rows[i].flags & KHUI_CW_ROW_TIMERSET) + { + KillTimer(tbl->hwnd, (UINT_PTR) &(tbl->rows[i])); + tbl->rows[i].flags &= ~KHUI_CW_ROW_TIMERSET; + } + } + + if(KHM_FAILED(kcdb_credset_get_size(tbl->credset, &n_creds))) + goto _exit; + + n_rows = 0; + prevcred = NULL; + ol = NULL; + + for(i=0; i < (int) n_creds; i++) { + if(KHM_FAILED(kcdb_credset_get_cred(tbl->credset, i, &thiscred))) + continue; + + /* if this credential appears to be the same as another for + this view, we skip it */ + if(prevcred) { + for(j=0; j < (int) tbl->n_cols; j++) { + if(kcdb_creds_comp_attr(prevcred, thiscred, tbl->cols[j].attr_id)) + break; + } + + if(j >= (int) tbl->n_cols) { + if (n_rows > 0) { + tbl->rows[n_rows - 1].idx_end = i; + } + continue; + } + } + + if(!prevcred) + level = 0; + else { + for(j=0; j < n_grouping; j++) { + /* determine the grouping level at which thiscred + differs from prevcred */ + if(kcdb_creds_comp_attr(prevcred,thiscred,tbl->cols[grouping[j]].attr_id)) + break; + } + level = j; + } + + /* now we have to walk up until we get to the parent of the + outline level we should be in */ + while(ol && ol->level >= level) { + ol->length = n_rows - ol->start; + ol->idx_end = i - 1; + ol = TPARENT(ol); + } + + if(ol) { + visible = (ol->flags & KHUI_CW_O_VISIBLE) && + (ol->flags & KHUI_CW_O_EXPAND); + selected = (ol->flags & KHUI_CW_O_SELECTED); + } else { + visible = TRUE; + selected = FALSE; + } + + /* now ol points to an outline node at the next highest level + or is NULL if level = 0 */ + + for(j=level; j < n_grouping; j++) { + khui_credwnd_outline * to; + /* now we search for an outline object at the next level + which matches the heading */ + cbbuf = sizeof(buf); + buf[0] = L'\0'; + if(KHM_FAILED + (kcdb_cred_get_attr_string(thiscred, + tbl->cols[grouping[j]].attr_id, + buf, &cbbuf, 0))) { + cbbuf = sizeof(wchar_t); + buf[0] = L'\0'; + } + + if(ol) + to = TFIRSTCHILD(ol); + else + to = tbl->outline; + + while(to) { + if(!wcscmp(buf, to->header)) + break; + to = LNEXT(to); + } + + if(to) { + /* found it */ + ol = to; + } else { + /* not found. create */ + to = cw_new_outline_node(buf); + if(ol) { + TADDCHILD(ol, to); + } else { + LPUSH(&(tbl->outline), to); + } + ol = to; + ol->flags = KHUI_CW_O_EXPAND; + ol->level = j; + ol->col = grouping[j]; + + if(tbl->cols[grouping[j]].attr_id == KCDB_ATTR_ID_NAME) { + khm_handle h; + if(KHM_SUCCEEDED(kcdb_identity_create(buf, 0, &h))) { + ol->attr_id = KCDB_ATTR_ID; + ol->data = (void *) h; + + /* the outline only lasts as long as the + credential, and the credential has a hold + on the identity. */ + kcdb_identity_release(h); + } + else + ol->data = 0; + } else if(tbl->cols[grouping[j]].attr_id == + KCDB_ATTR_TYPE_NAME) { + khm_int32 t; + ol->attr_id = KCDB_ATTR_TYPE; + if(KHM_SUCCEEDED(kcdb_cred_get_type(thiscred, &t))) + ol->data = (void *)(ssize_t) t; + else + ol->data = (void *)(ssize_t) KCDB_CREDTYPE_INVALID; + } else { + khm_int32 rv; + khm_int32 alt_id; + kcdb_attrib * attrib; + + rv = + kcdb_attrib_get_info(tbl->cols[grouping[j]].attr_id, + &attrib); + assert(KHM_SUCCEEDED(rv)); + + if (attrib->flags & KCDB_ATTR_FLAG_ALTVIEW) + alt_id = attrib->alt_id; + else + alt_id = tbl->cols[grouping[j]].attr_id; + + ol->attr_id = alt_id; + + kcdb_attrib_release_info(attrib); + + rv = kcdb_cred_get_attr(thiscred, + alt_id, + NULL, + NULL, + &cbbuf); + if (rv != KHM_ERROR_TOO_LONG || cbbuf == 0) { + ol->data = NULL; + } else { + ol->data = malloc(cbbuf); + assert(ol->data); + rv = kcdb_cred_get_attr(thiscred, + alt_id, + NULL, + ol->data, + &cbbuf); + assert(KHM_SUCCEEDED(rv)); + ol->cb_data = cbbuf; + ol->flags |= KHUI_CW_O_DATAALLOC; + } + } + } + + /* now ol points at the node at level j we want to be + in */ + ol->start = n_rows; + ol->idx_start = i; + ol->length = 0; + ol->flags &= ~CW_EXPSTATE_MASK; + ol->flags &= ~KHUI_CW_O_SHOWFLAG; + ol->flags &= ~KHUI_CW_O_STICKY; + + if(selected) { + ol->flags |= KHUI_CW_O_SELECTED; + } + if(visible) { + cw_set_tbl_row_header(tbl, n_rows, grouping[j], ol); + n_rows ++; + ol->flags |= KHUI_CW_O_VISIBLE; + } else { + ol->flags &= ~KHUI_CW_O_VISIBLE; + } + visible = visible && (ol->flags & KHUI_CW_O_EXPAND); + selected = (selected || (ol->flags & KHUI_CW_O_SELECTED)); + } + + /* we need to do this here too just in case we were already at + the level we were supposed to be in */ + visible = visible && (ol->flags & KHUI_CW_O_EXPAND); + + flags = cw_get_cred_exp_flags(tbl, thiscred); + + if(visible) { + khm_int32 c_flags; + + cw_set_tbl_row_cred(tbl, n_rows, thiscred, + grouping[n_grouping-1]); + kcdb_cred_get_flags(thiscred, &c_flags); + if(flags) { + tbl->rows[n_rows].flags |= flags; + } + if(selected || + (c_flags & KCDB_CRED_FLAG_SELECTED)) + tbl->rows[n_rows].flags |= KHUI_CW_ROW_SELECTED; + tbl->rows[n_rows].idx_start = i; + tbl->rows[n_rows].idx_end = i; + + n_rows++; + } else if(flags) { + khui_credwnd_outline *to; + /* the row that is flagged is not visible. We need to send + the flag upstream until we hit a visible outline node */ + to = ol; + while(to && !(to->flags & KHUI_CW_O_VISIBLE)) { + to = TPARENT(to); + } + if(to) { + to->flags |= KHUI_CW_O_SHOWFLAG; + } + } + + /* and we propagate the flags upstream */ + if(flags) { + khui_credwnd_outline *to; + + to = ol; + while(to) { + if((to->flags & CW_EXPSTATE_MASK) < flags) { + to->flags = (to->flags & ~CW_EXPSTATE_MASK) | flags; + } + to = TPARENT(to); + } + } + + if(prevcred) + kcdb_cred_release(prevcred); + prevcred = thiscred; + } + + while(ol) { + ol->length = n_rows - ol->start; + ol->idx_end = i - 1; + ol = TPARENT(ol); + } + + if(prevcred) { + kcdb_cred_release(prevcred); + prevcred = NULL; + } + + /* Add any sticky identities that we haven't seen yet */ + if (n_grouping > 0 && + tbl->cols[grouping[0]].attr_id == KCDB_ATTR_ID_NAME) { + + khui_credwnd_outline * o; + wchar_t * idnames = NULL; + wchar_t * t; + khm_size n_idents; + khm_size cb_names; + wchar_t ** idarray = NULL; + int i; + + if (kcdb_identity_enum(KCDB_IDENT_FLAG_STICKY, + KCDB_IDENT_FLAG_STICKY, + NULL, + &cb_names, + &n_idents) != KHM_ERROR_TOO_LONG || + n_idents == 0 || + cb_names == 0) + goto _cleanup_sticky; + + idnames = malloc(cb_names); + idarray = malloc(n_idents * sizeof(*idarray)); +#ifdef DEBUG + assert(idnames); + assert(idarray); +#endif + + if (KHM_FAILED(kcdb_identity_enum(KCDB_IDENT_FLAG_STICKY, + KCDB_IDENT_FLAG_STICKY, + idnames, + &cb_names, + &n_idents))) + goto _cleanup_sticky; + + for (i=0, t=idnames; t && *t; t = multi_string_next(t), i++) { + idarray[i] = t; + } + + qsort(idarray, n_idents, sizeof(*idarray), iwcscmp); + + for (i=0; i < (int) n_idents; i++) { + for (o = tbl->outline; o; o = LNEXT(o)) { + if (!wcscmp(idarray[i], o->header)) + break; + } + + if (o) { + /* found it */ + if (o->start != -1) /* already visible? */ + continue; + } else { + /* not found. create */ + o = cw_new_outline_node(idarray[i]); + o->flags = KHUI_CW_O_VISIBLE; + o->level = 0; + o->col = grouping[0]; + } + + o->flags |= KHUI_CW_O_STICKY; + o->flags &= ~KHUI_CW_O_EXPAND; + o->start = n_rows; + o->length = 1; + o->idx_start = -1; + + cw_set_tbl_row_header(tbl, n_rows, grouping[0], o); + + n_rows ++; + } + + _cleanup_sticky: + if (idnames) + free(idnames); + if (idarray) + free(idarray); + } + + tbl->n_rows = n_rows; + tbl->flags |= KHUI_CW_TBL_ROW_DIRTY; + + tbl->flags &= ~KHUI_CW_TBL_COL_DIRTY; +_exit: + if(grouping) + free(grouping); +} + +void +cw_unload_view(khui_credwnd_tbl * tbl) +{ +#define SafeDeleteObject(o) \ + do { \ + if(o) { \ + DeleteObject(o); \ + o = NULL; \ + } \ + } while(0) + + SafeDeleteObject(tbl->hf_header); + SafeDeleteObject(tbl->hf_normal); + SafeDeleteObject(tbl->hf_bold); + SafeDeleteObject(tbl->hf_bold_header); + SafeDeleteObject(tbl->hb_grey); + SafeDeleteObject(tbl->hb_normal); + SafeDeleteObject(tbl->hb_sel); + SafeDeleteObject(tbl->hb_hdr_bg); + SafeDeleteObject(tbl->hb_hdr_bg_sel); + SafeDeleteObject(tbl->hb_hdr_bg_crit); + SafeDeleteObject(tbl->hb_hdr_bg_exp); + SafeDeleteObject(tbl->hb_hdr_bg_warn); + +#undef SafeDeleteObject + + if(tbl->credset) { + kcdb_credset_delete(tbl->credset); + tbl->credset = NULL; + } + if(tbl->ilist) { + khui_delete_ilist(tbl->ilist); + tbl->ilist = NULL; + } + + if(tbl->cols) { + khm_size i; + for(i=0; i < tbl->n_cols; i++) { + if(tbl->cols[i].title) + free(tbl->cols[i].title); + Header_DeleteItem(tbl->hwnd_header, 0); + } + free(tbl->cols); + tbl->cols = NULL; + tbl->n_cols = 0; + tbl->n_total_cols = 0; + } + + if(tbl->rows) { + free(tbl->rows); + tbl->rows = NULL; + tbl->n_rows = 0; + tbl->n_total_rows = 0; + } + + khui_delete_bitmap(&tbl->kbm_logo_shade); +} + +void +cw_hditem_from_tbl_col(khui_credwnd_col * col, HDITEM *phi) +{ + size_t cchsize; + + phi->mask = HDI_FORMAT | HDI_LPARAM | HDI_WIDTH; + if(cw_is_custom_attr(col->attr_id)) { + if(col->attr_id == CW_CA_FLAGS) { + phi->fmt = 0; + } else if(col->attr_id == CW_CA_TYPEICON) { + phi->fmt = 0; + } else { + /* what the? */ + /*TODO: throw up and die */ + } + } else { + phi->mask |= HDI_TEXT; + phi->pszText = col->title; + StringCchLength(col->title, KCDB_MAXCCH_SHORT_DESC, &cchsize); + phi->cchTextMax = (int) cchsize; + phi->fmt = HDF_CENTER | HDF_STRING; + } + phi->lParam = col->attr_id; + if(col->flags & KHUI_CW_COL_SORT_INC) { + phi->fmt |= HDF_SORTUP; + } else if(col->flags & KHUI_CW_COL_SORT_DEC) { + phi->fmt |= HDF_SORTDOWN; + } + if(col->width < 0) { + /*TODO: come up with a better way to handle this case */ + col->width = 200; + } + phi->cxy = col->width; +} + +/* returns a bitmask indicating which measures were changed */ +int +cw_update_extents(khui_credwnd_tbl * tbl, + khm_boolean update_scroll) { + int ext_x, ext_y; + int i; + + ext_x = 0; + for(i=0; i < (int) tbl->n_cols; i++) { + tbl->cols[i].x = ext_x; + ext_x += tbl->cols[i].width; + } + + if(!tbl->cell_height) { + HDC dc; + HFONT hfold; + SIZE size; + size_t cbbuf; + wchar_t buf[64]; + + dc = GetWindowDC(tbl->hwnd); + if(tbl->hf_normal) + hfold = SelectFont(dc, tbl->hf_normal); + + LoadString(khm_hInstance, IDS_SAMPLE_STRING, buf, sizeof(buf)/sizeof(buf[0])); + StringCchLength(buf, sizeof(buf)/sizeof(buf[0]), &cbbuf); + GetTextExtentPoint32(dc, buf, (int) cbbuf, &size); + + if(tbl->hf_normal) + SelectFont(dc,hfold); + ReleaseDC(tbl->hwnd, dc); + + tbl->cell_height = size.cy + tbl->vpad * 2; + } + + ext_y = (int) tbl->n_rows * tbl->cell_height; + + tbl->ext_width = ext_x; + tbl->ext_height = ext_y; + + /* useful in the future when implementing variable height rows. + The KHUI_CW_TBL_ROW_DIRTY bit indicates that the rows have + changed and that the y extent has to be recalculated. */ + tbl->flags &= ~KHUI_CW_TBL_ROW_DIRTY; + + if(update_scroll) { + RECT r; + int cl_w; + int cl_h; + SCROLLINFO si; + WINDOWPOS pw; + HDLAYOUT hdl; + + /* update the header control first */ + +retry_update_scroll: + GetClientRect(tbl->hwnd, &r); + + cl_w = r.right - r.left; + cl_h = (r.bottom - r.top); + cl_h -= tbl->header_height; + + if(tbl->scr_top < 0 || tbl->ext_height < cl_h) + tbl->scr_top = 0; + else if(tbl->scr_top > tbl->ext_height - cl_h) + tbl->scr_top = tbl->ext_height - cl_h; + if(tbl->scr_left < 0 || tbl->ext_width < cl_w) + tbl->scr_left = 0; + else if(tbl->scr_left > tbl->ext_width - cl_w) + tbl->scr_left = tbl->ext_width - cl_w; + + /* adjustments for scrolling */ + r.left -= tbl->scr_left; + r.right = max(tbl->ext_width + r.left, r.right); + + hdl.prc = &r; + hdl.pwpos = &pw; + + Header_Layout(tbl->hwnd_header, &hdl); + + if(tbl->header_height == 0) { + tbl->header_height = pw.cy; + goto retry_update_scroll; + } else + tbl->header_height = pw.cy; + + SetWindowPos( + tbl->hwnd_header, + pw.hwndInsertAfter, + pw.x, + pw.y, + pw.cx, + pw.cy, + pw.flags); + + si.cbSize = sizeof(si); + si.nMin = 0; + si.nMax = tbl->ext_height; + si.nPage = cl_h; + si.nPos = tbl->scr_top; + si.fMask = SIF_ALL | SIF_DISABLENOSCROLL; + SetScrollInfo(tbl->hwnd, SB_VERT, &si, TRUE); + + si.cbSize = sizeof(si); + si.nMin = 0; + si.nMax = tbl->ext_width; + si.nPage = cl_w; + si.nPos = tbl->scr_left; + si.fMask = SIF_ALL | SIF_DISABLENOSCROLL; + SetScrollInfo(tbl->hwnd, SB_HORZ, &si, TRUE); + } + + return 0; +} + +void +cw_insert_header_cols(khui_credwnd_tbl * tbl) { + HWND hdr; + HDITEM hi; + int i; + + hdr = tbl->hwnd_header; + + for(i=0; i < (int) tbl->n_cols; i++) { + cw_hditem_from_tbl_col(&(tbl->cols[i]), &hi); + Header_InsertItem(hdr, 512, &hi); + } +} + +#define CW_ER_BLANK 0 +#define CW_ER_GREY 1 +#define CW_ER_SEL 2 + +void +cw_erase_rect(HDC hdc, + khui_credwnd_tbl * tbl, + RECT * r_wnd, + RECT * r_erase, + int type) +{ + RECT rlogo; + RECT ri; + RECT t; + BOOL rie; + HBRUSH hbr; + + if(RectVisible(hdc, r_erase)) { + + switch(type) { + case CW_ER_BLANK: + hbr = tbl->hb_normal; + break; + + case CW_ER_GREY: + hbr = tbl->hb_grey; + break; + + case CW_ER_SEL: + hbr = tbl->hb_sel; + break; + + default: + return; + } + + if(tbl->kbm_logo_shade.cx != -1 && type == CW_ER_BLANK) { + rlogo.left = r_wnd->right - tbl->kbm_logo_shade.cx; + rlogo.right = r_wnd->right; + rlogo.top = r_wnd->bottom - tbl->kbm_logo_shade.cy; + rlogo.bottom = r_wnd->bottom; + rie = IntersectRect(&ri, r_erase, &rlogo); + } else { + rie = FALSE; + } + + if(!rie) { + FillRect(hdc, r_erase, hbr); + } else { + HDC hdcb = CreateCompatibleDC(hdc); + HBITMAP hbmold = SelectObject(hdcb, tbl->kbm_logo_shade.hbmp); + + BitBlt(hdc, ri.left, ri.top, ri.right - ri.left, ri.bottom - ri.top, + hdcb, ri.left - rlogo.left, ri.top - rlogo.top, SRCCOPY); + + SelectObject(hdcb, hbmold); + DeleteDC(hdcb); + + if(r_erase->top < ri.top && r_erase->left < ri.left) { + t.left = r_erase->left; + t.top = r_erase->top; + t.right = ri.left; + t.bottom = ri.top; + FillRect(hdc, &t, hbr); + } + + if(r_erase->left < ri.left) { + t.left = r_erase->left; + t.top = ri.top; + t.right = ri.left; + t.bottom = ri.bottom; + FillRect(hdc, &t, hbr); + } + + if(r_erase->top < ri.top) { + t.left = ri.left; + t.top = r_erase->top; + t.right = ri.right; + t.bottom = ri.top; + FillRect(hdc, &t, hbr); + } + } + } +} + +void +cw_draw_header(HDC hdc, + khui_credwnd_tbl * tbl, + int row, + RECT * r) +{ + int colattr; + HPEN pl, pold; + khui_credwnd_row * cr; + khui_credwnd_outline * o; + int selected = 0; + + /* each header consists of a 'expose' widget and some text */ + /* we need to figure out the background color first */ + + cr = &(tbl->rows[row]); + o = (khui_credwnd_outline *) cr->data; + + colattr = tbl->cols[cr->col].attr_id; + + selected = o->flags & KHUI_CW_O_SELECTED; + + { + HBRUSH hbr; + if(selected) + hbr = tbl->hb_hdr_bg_sel; + else if((o->flags & CW_EXPSTATE_MASK) == CW_EXPSTATE_EXPIRED) + hbr = tbl->hb_hdr_bg_exp; + else if((o->flags & CW_EXPSTATE_MASK) == CW_EXPSTATE_CRITICAL) + hbr = tbl->hb_hdr_bg_crit; + else if((o->flags & CW_EXPSTATE_MASK) == CW_EXPSTATE_WARN) + hbr = tbl->hb_hdr_bg_warn; + else + hbr = tbl->hb_hdr_bg; + + FillRect(hdc, r, hbr); + } + + pl = CreatePen(PS_SOLID, 0, tbl->cr_hdr_outline); + pold = SelectObject(hdc, pl); + MoveToEx(hdc, r->left, r->bottom - 1, NULL); + LineTo(hdc,r->right,r->bottom - 1); + SelectObject(hdc, pold); + DeleteObject(pl); + + if (o->flags & KHUI_CW_O_STICKY) { + /* khui_ilist_draw_id(tbl->ilist, IDB_TK_NEW_SM, hdc, + r->left, r->bottom - KHUI_SMICON_CY, 0); */ + } else if((tbl->mouse_state & CW_MOUSE_OUTLINE) && tbl->mouse_row == row) { + if(o->flags & KHUI_CW_O_EXPAND) { + khui_ilist_draw_id(tbl->ilist, IDB_WDG_EXPAND_HI, hdc, r->left, r->bottom - KHUI_SMICON_CY, 0); + } else { + khui_ilist_draw_id(tbl->ilist, IDB_WDG_COLLAPSE_HI, hdc, r->left, r->bottom - KHUI_SMICON_CY, 0); + } + } else { + if(o->flags & KHUI_CW_O_EXPAND) { + khui_ilist_draw_id(tbl->ilist, IDB_WDG_EXPAND, hdc, r->left, r->bottom - KHUI_SMICON_CY, 0); + } else { + khui_ilist_draw_id(tbl->ilist, IDB_WDG_COLLAPSE, hdc, r->left, r->bottom - KHUI_SMICON_CY, 0); + } + } + + r->left += KHUI_SMICON_CX * 2; + + /* try to draw the icon, if there is one */ + if(colattr == KCDB_ATTR_ID_NAME) { + khui_ilist_draw_id(tbl->ilist, + ((o->flags & KHUI_CW_O_STICKY)? + IDB_ID_DIS_SM: + IDB_ID_SM), + hdc, + r->left, r->bottom - KHUI_SMICON_CY, + 0); + r->left += KHUI_SMICON_CX ; + } + + /* ok, now o->header contains the string representation of the + outline value */ + /* for now just write out the value */ + SetTextAlign(hdc, TA_BOTTOM | TA_LEFT); + + if(selected) + SetTextColor(hdc, tbl->cr_hdr_sel); + else + SetTextColor(hdc, tbl->cr_hdr_normal); + + TextOut(hdc, r->left, r->bottom - tbl->vpad, o->header, (int) wcslen(o->header)); +} + +LRESULT +cw_handle_header_msg(khui_credwnd_tbl * tbl, LPNMHEADER ph) { + RECT r; + HDITEM hi; + + switch(ph->hdr.code) { + /*TODO:Make it track smoother */ + case HDN_BEGINTRACK: + { + if(tbl->cols[ph->iItem].flags & KHUI_CW_COL_FIXED_WIDTH) + return TRUE; + else + return FALSE; + } + + case HDN_TRACK: + case HDN_ENDTRACK: + { + int width; + hi.mask = HDI_ORDER; + Header_GetItem(ph->hdr.hwndFrom, ph->iItem, &hi); + Header_GetItemRect(ph->hdr.hwndFrom, ph->iItem, &r); + width = r.right - r.left; + if(width != tbl->cols[hi.iOrder].width) { + tbl->cols[hi.iOrder].width = width; + cw_update_extents(tbl, TRUE); + InvalidateRect(tbl->hwnd, NULL, FALSE); + } + } + break; + + case NM_CUSTOMDRAW: + { + LPNMCUSTOMDRAW cd; + int idx; + + cd = (LPNMCUSTOMDRAW) ph; + switch(cd->dwDrawStage) + { + case CDDS_PREPAINT: + return CDRF_NOTIFYITEMDRAW; + + case CDDS_ITEMPREPAINT: + return CDRF_NOTIFYPOSTPAINT; + + case CDDS_ITEMPOSTPAINT: + if(cd->lItemlParam == CW_CA_FLAGS) + idx = IDB_WDG_FLAG; + else if(cd->lItemlParam == CW_CA_TYPEICON) + idx = IDB_WDG_CREDTYPE; + else + idx = -1; + + khui_ilist_draw_id(tbl->ilist, idx, cd->hdc, cd->rc.left, cd->rc.top, 0); + return 0; + } + } + break; + } + return 0; +} + +LRESULT +cw_wm_create(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + khui_credwnd_tbl * tbl; + + kmq_subscribe_hwnd(KMSG_CRED, hwnd); + + tbl = malloc(sizeof(*tbl)); + ZeroMemory(tbl, sizeof(*tbl)); + + /* some versions of VC generate portability warnings for + SetWindowLongPtr */ +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, 0, (LONG_PTR) tbl); +#pragma warning(pop) + + tbl->hwnd_header = CreateWindowEx( + 0, + WC_HEADER, + (LPWSTR) NULL, + WS_CHILD | HDS_BUTTONS | + HDS_FULLDRAG | HDS_HORZ | HDS_HOTTRACK | HDS_FLAT, + 0,0,0,0,hwnd, (HMENU) 0, khm_hInstance, NULL); + + cw_load_view(tbl, NULL /* default view */, hwnd); + cw_insert_header_cols(tbl); + + cw_update_creds(tbl); + cw_update_outline(tbl); + cw_update_extents(tbl, FALSE); + + { + RECT rect; + WINDOWPOS pw; + HDLAYOUT hdl; + + hdl.prc = ▭ + hdl.pwpos = &pw; + GetClientRect(hwnd, &rect); + + Header_Layout(tbl->hwnd_header, &hdl); + + SetWindowPos( + tbl->hwnd_header, + pw.hwndInsertAfter, + pw.x, + pw.y, + pw.cx, + pw.cy, + pw.flags | SWP_SHOWWINDOW); + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +LRESULT +cw_wm_destroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + khui_credwnd_tbl * tbl; + + kmq_unsubscribe_hwnd(KMSG_CRED, hwnd); + + tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + + cw_unload_view(tbl); + + free(tbl); + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +LRESULT +cw_wm_paint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + khui_credwnd_tbl * tbl; + HDC hdc; + PAINTSTRUCT ps; + RECT r,rh; + HFONT hf_old; + int row_s, row_e; + int col_s, col_e; + int i,j,x,y,xs,xe,ys,ye; + int flag_col = -1; + int d_x = -1; + int selected = 0; + + tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + + if(!GetUpdateRect(hwnd, &r, FALSE)) + goto _exit; + + hdc = BeginPaint(hwnd, &ps); + if(tbl->hf_normal) + hf_old = SelectFont(hdc, tbl->hf_normal); + SetTextAlign(hdc, TA_LEFT | TA_TOP | TA_NOUPDATECP); + SetBkMode(hdc, TRANSPARENT); + + GetClientRect(hwnd,&r); + r.top += tbl->header_height; + + if(tbl->n_rows) { + /* remove the notification window if there is one */ + if(tbl->hwnd_notif) { + DestroyWindow(tbl->hwnd_notif); + tbl->hwnd_notif = NULL; + } + /* we compute the visible area in terms of rows and columns */ + /* row_s : first visible row */ + /* col_s : first visible column */ + /* row_e : last visible row */ + /* col_e : last visible column */ + /* ys : top edge of first visible row */ + /* xs : left edge of first visible column */ + + /* We *NEED* all the meta columns to be on the left */ + + row_s = tbl->scr_top / tbl->cell_height; + ys = row_s * tbl->cell_height; + row_e = (tbl->scr_top + (r.bottom - r.top)) / tbl->cell_height + 1; + if(row_e > (int) tbl->n_rows) + row_e = (int) tbl->n_rows; + x = 0; + col_s = -1; + col_e = -1; + xs = 0; + for(i=0; i < (int) tbl->n_cols; i++) { + if(col_e == -1 && x >= tbl->scr_left + (r.right - r.left)) { + col_e = i; + } + if(tbl->cols[i].attr_id == CW_CA_FLAGS) + flag_col = i; + if(d_x == -1 && !cw_is_custom_attr(tbl->cols[i].attr_id)) + d_x = x; + x += tbl->cols[i].width; + if(col_s == -1 && x > tbl->scr_left) { + col_s = i; + xs = tbl->cols[i].x; + } + } + + if(col_e == -1) + col_e = i; + + if(col_s == -1) + col_s = i; + + if(d_x != -1) + d_x += r.left - tbl->scr_left; + + xs += r.left - tbl->scr_left; + ys += r.top - tbl->scr_top; + xe = r.left + tbl->ext_width - tbl->scr_left; + ye = r.top + tbl->ext_height - tbl->scr_top; + + /* now draw */ + y = ys; + for(i=row_s; i < row_e; i++) { + selected = tbl->rows[i].flags & KHUI_CW_ROW_SELECTED; + + if(tbl->cursor_row == i) + SelectFont(hdc, tbl->hf_bold); + + x = xs; + if(tbl->rows[i].flags & KHUI_CW_ROW_HEADER) { + rh.left = xs; + rh.right = xs; + for(j=col_s; j < tbl->rows[i].col; j++) + rh.right += tbl->cols[j].width; + rh.top = y; + rh.bottom = y + tbl->cell_height; + if(rh.right > rh.left) { + cw_erase_rect(hdc, tbl, &r, &rh, (selected)?CW_ER_SEL:CW_ER_BLANK); + } + rh.left = rh.right; + rh.right = xe; + + cw_draw_header(hdc, tbl, i, &rh); + } + + if(selected) + SetTextColor(hdc, tbl->cr_sel); + else + SetTextColor(hdc, tbl->cr_normal); + + x = xs; + rh.top = y; + rh.bottom = y + tbl->cell_height; + for(j=col_s; j < col_e; x += tbl->cols[j++].width) { + wchar_t buf[256]; + khm_size cbbuf; + + rh.left = x; + rh.right = x + tbl->cols[j].width; + + if(!RectVisible(hdc, &rh)) + continue; + + if(!cw_is_custom_attr(tbl->cols[j].attr_id)) { + if(!(tbl->rows[i].flags & KHUI_CW_ROW_HEADER)) { + cw_erase_rect(hdc, tbl, &r, &rh, (selected)?CW_ER_SEL:CW_ER_BLANK); + + if(j > tbl->rows[i].col) { + cbbuf = sizeof(buf); + if(KHM_FAILED(kcdb_cred_get_attr_string((khm_handle) tbl->rows[i].data, tbl->cols[j].attr_id, buf, &cbbuf, KCDB_TS_SHORT))) + continue; + + rh.left += tbl->hpad; + rh.right -= tbl->hpad; + + SetTextAlign(hdc, 0); + DrawText(hdc, buf, (int)((cbbuf / sizeof(wchar_t)) - 1), &rh, DT_LEFT | DT_VCENTER | DT_NOCLIP | DT_SINGLELINE | DT_END_ELLIPSIS); + //TextOut(hdc, x, y + tbl->vpad, buf, (cbbuf / sizeof(wchar_t)) - 1); + } + } + } else { + cw_erase_rect(hdc, tbl, &r, &rh, (selected)?CW_ER_SEL:CW_ER_BLANK); + + if(tbl->cols[j].attr_id == CW_CA_FLAGS) { + khui_credwnd_outline * o; + khm_int32 flag; + + if(tbl->rows[i].flags & KHUI_CW_ROW_HEADER) { + o = ((khui_credwnd_outline *) tbl->rows[i].data); + if(o->flags & KHUI_CW_O_SHOWFLAG) + flag = o->flags; + else + flag = 0; + } + else + flag = tbl->rows[i].flags; + + flag &= CW_EXPSTATE_MASK; + + if(flag == CW_EXPSTATE_WARN) { + khui_ilist_draw_id(tbl->ilist, IDB_FLAG_WARN, hdc, x, y, 0); + } else if(flag == CW_EXPSTATE_CRITICAL) { + khui_ilist_draw_id(tbl->ilist, IDB_FLAG_CRITICAL, hdc, x, y, 0); + } else if(flag == CW_EXPSTATE_EXPIRED) { + khui_ilist_draw_id(tbl->ilist, IDB_FLAG_EXPIRED, hdc, x, y, 0); + } else { + khm_int32 flags; + + if (KHM_SUCCEEDED(kcdb_cred_get_flags((khm_handle) tbl->rows[i].data, &flags)) && + (flags & KCDB_CRED_FLAG_RENEWABLE)) { + khui_ilist_draw_id(tbl->ilist, + IDB_TK_REFRESH_SM, + hdc, + x, y, 0); + } + } + } + } + } + + if(tbl->cursor_row == i) { + rh.left = tbl->scr_left; + rh.right = tbl->scr_left + tbl->ext_width; + + DrawFocusRect(hdc, &rh); + + SelectFont(hdc, tbl->hf_normal); + } + + y += tbl->cell_height; + + } + + if(xe < r.right) { + rh.left = xe; + rh.right = r.right; + rh.top = r.top; + rh.bottom = r.bottom; + + cw_erase_rect(hdc, tbl, &r, &rh, CW_ER_BLANK); + } + + if(ye < r.bottom) { + rh.left = r.left; + rh.right = (xe < r.right)?xe:r.right; + rh.top = ye; + rh.bottom = r.bottom; + + cw_erase_rect(hdc, tbl, &r, &rh, CW_ER_BLANK); + } + + } else { + wchar_t buf[512]; + cw_erase_rect(hdc, tbl, &r, &r, CW_ER_BLANK); + + if(tbl->hwnd_notif == NULL) { + LoadString(khm_hInstance, IDS_NO_CREDS, buf, sizeof(buf)/sizeof(buf[0])); + tbl->hwnd_notif = khm_create_htwnd( + tbl->hwnd, + buf, + r.left,r.top,r.right - r.left,(r.bottom - r.top) /2, + WS_EX_TRANSPARENT, + WS_VISIBLE); + if(tbl->hwnd_notif) { + SendMessage(tbl->hwnd_notif, WM_SETFONT, (WPARAM) tbl->hf_normal, (LPARAM) FALSE); + ShowWindow(tbl->hwnd_notif, SW_SHOW); + } + } + } + + if(tbl->hf_normal) + SelectFont(hdc, hf_old); + + EndPaint(hwnd,&ps); +_exit: + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +LRESULT +cw_wm_size(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + RECT rect; + khui_credwnd_tbl * tbl; + + tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + + cw_update_extents(tbl, TRUE); + + GetClientRect(hwnd, &rect); + + if(tbl->hwnd_notif) { + SetWindowPos( + tbl->hwnd_notif, + tbl->hwnd_header, + rect.left, + tbl->header_height, + rect.right - rect.left, + (rect.bottom - tbl->header_height) / 2, + 0); + } + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +LRESULT +cw_wm_notify(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + khui_credwnd_tbl * tbl; + LPNMHDR pnmh; + + tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + pnmh = (LPNMHDR) lParam; + if(pnmh->hwndFrom == tbl->hwnd_header) { + LPNMHEADER ph; + ph = (LPNMHEADER) lParam; + return cw_handle_header_msg(tbl, ph); + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +static void cw_pp_begin(khui_property_sheet * s); +static void cw_pp_precreate(khui_property_sheet * s); +static void cw_pp_end(khui_property_sheet * s); +static void cw_pp_destroy(khui_property_sheet *ps); + +LRESULT +cw_kmq_wm_dispatch(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + kmq_message * m; + khm_int32 rv = KHM_ERROR_SUCCESS; + khui_credwnd_tbl * tbl; + + tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + + kmq_wm_begin(lParam, &m); + if(m->type == KMSG_CRED) { + if(m->subtype == KMSG_CRED_ROOTDELTA) { + cw_update_creds(tbl); + cw_update_outline(tbl); + cw_update_extents(tbl, TRUE); + InvalidateRect(hwnd, NULL, FALSE); + } else if(m->subtype == KMSG_CRED_PP_BEGIN) { + cw_pp_begin((khui_property_sheet *) m->vparam); + } else if(m->subtype == KMSG_CRED_PP_PRECREATE) { + cw_pp_precreate((khui_property_sheet *) m->vparam); + } else if(m->subtype == KMSG_CRED_PP_END) { + cw_pp_end((khui_property_sheet *) m->vparam); + } else if(m->subtype == KMSG_CRED_PP_DESTROY) { + cw_pp_destroy((khui_property_sheet *) m->vparam); + } + } + return kmq_wm_end(m, rv); +} + +static void +cw_select_outline_level(khui_credwnd_outline * o, + BOOL select) +{ + while(o) { + if (select) + o->flags |= KHUI_CW_O_SELECTED; + else + o->flags &= ~KHUI_CW_O_SELECTED; + cw_select_outline_level(TFIRSTCHILD(o), select); + o = LNEXT(o); + } +} + +static void +cw_select_outline(khui_credwnd_outline * o, + BOOL select) +{ + if (select) + o->flags |= KHUI_CW_O_SELECTED; + else + o->flags &= ~KHUI_CW_O_SELECTED; +} + +static void +cw_unselect_all(khui_credwnd_tbl * tbl) +{ + khm_size i; + + for(i=0; i<tbl->n_rows; i++) { + tbl->rows[i].flags &= ~KHUI_CW_ROW_SELECTED; + if (!(tbl->rows[i].flags & KHUI_CW_ROW_HEADER)) + kcdb_cred_set_flags((khm_handle) tbl->rows[i].data, + 0, + KCDB_CRED_FLAG_SELECTED); + } + + cw_select_outline_level(tbl->outline, FALSE); +} + +static void +cw_update_outline_selection_state(khui_credwnd_tbl * tbl, + khui_credwnd_outline * o) +{ + BOOL select = TRUE; + int j; + + for (j = o->start + 1; j < o->start + o->length; j++) { + if (tbl->rows[j].flags & KHUI_CW_ROW_HEADER) { + cw_update_outline_selection_state(tbl, + (khui_credwnd_outline *) + tbl->rows[j].data); + } + + if (!(tbl->rows[j].flags & KHUI_CW_ROW_SELECTED)) { + select = FALSE; + } + + if (tbl->rows[j].flags & KHUI_CW_ROW_HEADER) { + j += ((khui_credwnd_outline *) tbl->rows[j].data)->length - 1; + } + } + + /* special case : the header has been collapsed and we are just + using one row. In this case, the for loop above will do + nothing. */ + + if (o->length == 1) { + select = (tbl->rows[o->start].flags & KHUI_CW_ROW_SELECTED); + } + + cw_select_outline(o, select); + + if (select) { + tbl->rows[o->start].flags |= KHUI_CW_ROW_SELECTED; + } else { + tbl->rows[o->start].flags &= ~KHUI_CW_ROW_SELECTED; + } +} + +static void +cw_update_selection_state(khui_credwnd_tbl * tbl) +{ + khm_size i; + + cw_select_outline_level(tbl->outline, FALSE); + + for (i=0; i < tbl->n_rows; i++) { + if (tbl->rows[i].flags & KHUI_CW_ROW_HEADER) { + khui_credwnd_outline * o; + + o = (khui_credwnd_outline *) tbl->rows[i].data; + + cw_update_outline_selection_state(tbl, o); + + i += o->length - 1; + } + } +} + +/* Examine the current row and set the UI context */ +static void +cw_set_row_context(khui_credwnd_tbl * tbl, int row) +{ + khui_credwnd_outline * o; + BOOL set_context = TRUE; + + if (tbl->rows[row].flags & KHUI_CW_ROW_HEADER) { + + o = (khui_credwnd_outline *) tbl->rows[row].data; + + if (tbl->cols[o->col].attr_id == KCDB_ATTR_ID_NAME) { + if (TPARENT(o) == NULL) { /* selected an identity */ + khui_context_set(KHUI_SCOPE_IDENT, + (khm_handle) o->data, + KCDB_CREDTYPE_INVALID, + NULL, + NULL, + 0, + tbl->credset); + } else { + khui_credwnd_outline * op; + + op = TPARENT(o); + + if (tbl->cols[op->col].attr_id == KCDB_ATTR_TYPE_NAME && + TPARENT(op) == NULL) { + /* selected a credential type */ + khui_context_set(KHUI_SCOPE_CREDTYPE, + (khm_handle) o->data, + (khm_int32) (DWORD_PTR) op->data, + NULL, + NULL, + 0, + tbl->credset); + } else { + set_context = FALSE; + } + } + } else if (tbl->cols[o->col].attr_id == KCDB_ATTR_TYPE_NAME) { + if (TPARENT(o) == NULL) { + /* selected an entire cred type */ + khui_context_set(KHUI_SCOPE_CREDTYPE, + NULL, + (khm_int32) (DWORD_PTR) o->data, + NULL, + NULL, + 0, + tbl->credset); + } else { + khui_credwnd_outline * op; + + op = TPARENT(o); + if (tbl->cols[op->col].attr_id == KCDB_ATTR_ID_NAME && + TPARENT(op) == NULL) { + /* credtype under an identity */ + khui_context_set(KHUI_SCOPE_CREDTYPE, + (khm_handle) op->data, + (khm_int32) (DWORD_PTR) o->data, + NULL, + NULL, + 0, + tbl->credset); + } else { + set_context = FALSE; + } + } + } else { + set_context = FALSE; + } + + if (!set_context) { + /* woohoo. cred group. yay. */ + khui_header headers[KHUI_MAX_HEADERS]; + khm_size n_headers = 0; + + do { + headers[n_headers].attr_id = + o->attr_id; + if (tbl->cols[o->col].attr_id == + KCDB_ATTR_ID_NAME) { + headers[n_headers].data = &(o->data); + headers[n_headers].cb_data = sizeof(khm_handle); + } else if (tbl->cols[o->col].attr_id == + KCDB_ATTR_TYPE_NAME) { + headers[n_headers].data = &(o->data); + headers[n_headers].cb_data = sizeof(khm_int32); + } else { + headers[n_headers].data = o->data; + headers[n_headers].cb_data = o->cb_data; + } + + n_headers++; + + o = TPARENT(o); + } while(o); + + khui_context_set(KHUI_SCOPE_GROUP, + NULL, + KCDB_CREDTYPE_INVALID, + NULL, + headers, + n_headers, + tbl->credset); + } + + } else { + khm_handle cred; + + cred = (khm_handle) tbl->rows[row].data; + + khui_context_set(KHUI_SCOPE_CRED, + NULL, + KCDB_CREDTYPE_INVALID, + cred, + NULL, + 0, + tbl->credset); + } +} + +static void +cw_select_row(khui_credwnd_tbl * tbl, int row, WPARAM wParam) +{ + int i; + BOOL toggle; + BOOL extend; + int group_begin; + int group_end; + + if (wParam & MK_CONTROL) { + toggle = TRUE; + extend = FALSE; + } else if (wParam & MK_SHIFT) { + toggle = FALSE; + extend = TRUE; + } else { + toggle = FALSE; + extend = FALSE; + } + + if (row < 0 || row >= (int) tbl->n_rows) + return; + + if (tbl->rows[row].flags & KHUI_CW_ROW_HEADER) { + khui_credwnd_outline * o; + + o = (khui_credwnd_outline *) tbl->rows[row].data; + + group_begin = o->start; + group_end = o->start + o->length - 1; + } else { + group_begin = row; + group_end = row; + } + + if (!toggle && !extend) { + /* selecting a single row */ + cw_unselect_all(tbl); + + tbl->cursor_row = row; + tbl->anchor_row = row; + + for (i = group_begin; i <= group_end; i++) { + tbl->rows[i].flags |= KHUI_CW_ROW_SELECTED; + if (!(tbl->rows[i].flags & KHUI_CW_ROW_HEADER)) { + kcdb_cred_set_flags((khm_handle) tbl->rows[i].data, + KCDB_CRED_FLAG_SELECTED, + KCDB_CRED_FLAG_SELECTED); + } + } + } else if (toggle) { + BOOL select; + + tbl->cursor_row = row; + tbl->anchor_row = row; + + select = !(tbl->rows[row].flags & KHUI_CW_ROW_SELECTED); + + for (i = group_begin; i <= group_end; i++) { + if (select) + tbl->rows[i].flags |= KHUI_CW_ROW_SELECTED; + else + tbl->rows[i].flags &= ~KHUI_CW_ROW_SELECTED; + + if (!(tbl->rows[i].flags & KHUI_CW_ROW_HEADER)) { + kcdb_cred_set_flags((khm_handle) tbl->rows[i].data, + (select)?KCDB_CRED_FLAG_SELECTED:0, + KCDB_CRED_FLAG_SELECTED); + } + + } + } else if (extend) { + int range_begin; + int range_end; + + cw_unselect_all(tbl); + + range_begin = min(row, tbl->anchor_row); + range_end = max(row, tbl->anchor_row); + + for (i = range_begin; i <= range_end; i++) { + tbl->rows[i].flags |= KHUI_CW_ROW_SELECTED; + + if (!(tbl->rows[i].flags & KHUI_CW_ROW_HEADER)) { + kcdb_cred_set_flags((khm_handle) tbl->rows[i].data, + KCDB_CRED_FLAG_SELECTED, + KCDB_CRED_FLAG_SELECTED); + } + } + + tbl->cursor_row = row; + } + + cw_update_selection_state(tbl); + + cw_set_row_context(tbl, tbl->cursor_row); + + InvalidateRect(tbl->hwnd, NULL, FALSE); +} + +static void +cw_toggle_outline_state(khui_credwnd_tbl * tbl, + khui_credwnd_outline * o) { + + int old_range_begin; + int old_range_end; + int new_range_begin; + int new_range_end; + + old_range_begin = o->start; + old_range_end = o->start + o->length - 1; + + o->flags ^= KHUI_CW_O_EXPAND; + + cw_update_outline(tbl); + cw_update_extents(tbl, TRUE); + + new_range_begin = o->start; + new_range_end = o->start + o->length - 1; + + if (tbl->cursor_row > old_range_end) { + tbl->cursor_row -= old_range_end - new_range_end; + } else if (tbl->cursor_row >= old_range_begin && + tbl->cursor_row <= old_range_end) { + tbl->cursor_row = new_range_begin; + } + + if (tbl->anchor_row > old_range_end) { + tbl->anchor_row -= old_range_end - new_range_end; + } else if (tbl->anchor_row >= old_range_begin && + tbl->anchor_row <= old_range_end) { + tbl->anchor_row = new_range_begin; + } + + InvalidateRect(tbl->hwnd, NULL, TRUE); + +} + +LRESULT cw_properties(HWND hwnd); + +LRESULT +cw_wm_mouse(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + khui_credwnd_tbl * tbl; + int x,y; + RECT r; + int row; + int col; + int i; + int nm_state,nm_row,nm_col; + + tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + + /* we are basically trying to capture events where the mouse is + hovering over one of the 'hotspots'. There are two kinds of + hotspots one is the little widget thinggy that you click on to + expand or collapse an outline. The other is a text cell that is + partially concealed. + */ + + x = GET_X_LPARAM(lParam); + y = GET_Y_LPARAM(lParam); + x += tbl->scr_left; + y += tbl->scr_top - tbl->header_height; + + row = y / tbl->cell_height; + col = -1; + nm_state = CW_MOUSE_NONE; + nm_row = nm_col = -1; + for(i=0; i < (int) tbl->n_cols; i++) { + if(x >= tbl->cols[i].x && + x < tbl->cols[i].x + tbl->cols[i].width) + { + col = i; + break; + } + } + + if(wParam & MK_LBUTTON) + nm_state = CW_MOUSE_LDOWN; + + if(row >= 0 && row < (int) tbl->n_rows) { + nm_state |= CW_MOUSE_ROW; + nm_row = row; + nm_col = col; + if(tbl->rows[row].flags & KHUI_CW_ROW_HEADER) { + /* are we on a widget then? */ + x -= tbl->cols[tbl->rows[row].col].x; + if(x >= 0 && x < KHUI_SMICON_CX) /* hit */ { + nm_state |= CW_MOUSE_OUTLINE; + } + } + } + + if((tbl->mouse_state & CW_MOUSE_LDOWN) && + (tbl->mouse_state & CW_MOUSE_OUTLINE) && + (nm_row != tbl->mouse_row)) + { + nm_state &= ~CW_MOUSE_OUTLINE; + } + + if(!(nm_state & CW_MOUSE_LDOWN) && + (tbl->mouse_state & CW_MOUSE_LDOWN)) { + + if((nm_state & CW_MOUSE_OUTLINE) && + (tbl->mouse_state & CW_MOUSE_OUTLINE)) { + /* click on a widget */ + khui_credwnd_outline * o; + + o = (khui_credwnd_outline *) tbl->rows[nm_row].data; + tbl->mouse_state = CW_MOUSE_OUTLINE; + + cw_toggle_outline_state(tbl, o); + + return 0; + } else if(nm_row == tbl->mouse_row) { + /* click on a row */ + cw_select_row(tbl, nm_row, wParam); + } + + } + + /*TODO: if a user clicks somewhere and drags on to an exand widget, it activates the widet. should not */ + + /* ok, now if we are changing state, we need to invalidate a few + regions */ + if((tbl->mouse_state ^ nm_state) & CW_MOUSE_OUTLINE) { + if(tbl->mouse_state & CW_MOUSE_OUTLINE) { + r.left = tbl->cols[tbl->mouse_col].x - tbl->scr_left; + r.top = tbl->mouse_row * tbl->cell_height + tbl->header_height - tbl->scr_top; + r.right = r.left + KHUI_SMICON_CX; + r.bottom = r.top + tbl->cell_height; + InvalidateRect(tbl->hwnd, &r, TRUE); + } + + tbl->mouse_col = nm_col; + tbl->mouse_row = nm_row; + tbl->mouse_state = nm_state; + + /* same code block as above */ + if(tbl->mouse_state & CW_MOUSE_OUTLINE) { + r.left = tbl->cols[tbl->mouse_col].x - tbl->scr_left; + r.top = tbl->mouse_row * tbl->cell_height + tbl->header_height - tbl->scr_top; + r.right = r.left + KHUI_SMICON_CX; + r.bottom = r.top + tbl->cell_height; + InvalidateRect(tbl->hwnd, &r, TRUE); + } + } else if(tbl->mouse_state != nm_state) { + tbl->mouse_col = nm_col; + tbl->mouse_row = nm_row; + tbl->mouse_state = nm_state; + } + + /* if it was a double click, also show the property + window */ + if (uMsg == WM_LBUTTONDBLCLK) { + cw_properties(hwnd); + } + + return 0; +} + +LRESULT +cw_wm_hscroll(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + khui_credwnd_tbl * tbl; + SCROLLINFO si; + RECT cr; + RECT lr; + RECT sr; + int dx; + int newpos; + + tbl = (khui_credwnd_tbl *) (LONG_PTR) GetWindowLongPtr(hwnd, 0); + GetClientRect(hwnd, &cr); + dx = tbl->scr_left; + + switch(LOWORD(wParam)) { + case SB_LEFT: + newpos = 0; + break; + + case SB_RIGHT: + newpos = tbl->ext_width; + break; + + case SB_LINELEFT: + newpos = tbl->scr_left - (tbl->ext_width / 12); + break; + + case SB_LINERIGHT: + newpos = tbl->scr_left + (tbl->ext_width / 12); + break; + + case SB_PAGELEFT: + newpos = tbl->scr_left - (cr.right - cr.left); + break; + + case SB_PAGERIGHT: + newpos = tbl->scr_left + (cr.right - cr.left); + break; + + case SB_THUMBTRACK: + case SB_THUMBPOSITION: + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_TRACKPOS; + GetScrollInfo(hwnd, SB_HORZ, &si); + + newpos = si.nTrackPos; + break; + + default: + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + + //cr.top += tbl->header_height; + tbl->scr_left = newpos; + cw_update_extents(tbl, TRUE); + + dx -= tbl->scr_left; + + /* exclude the watermark */ + lr.bottom = cr.bottom; + lr.right = cr.right; + lr.top = max(cr.bottom - tbl->kbm_logo_shade.cy, cr.top); + lr.left = max(cr.right - tbl->kbm_logo_shade.cx, cr.left); + + if(cr.top < lr.top && cr.left < cr.right) { + sr.left = cr.left; + sr.right = cr.right; + sr.top = cr.top; + sr.bottom = lr.top; + ScrollWindowEx( + hwnd, + dx, + 0, + &sr, + &sr, + NULL, + NULL, + SW_INVALIDATE | SW_SCROLLCHILDREN); + } + + if(cr.left < lr.left && lr.top < lr.bottom) { + sr.left = cr.left; + sr.right = lr.left; + sr.top = lr.top; + sr.bottom = lr.bottom; + ScrollWindowEx( + hwnd, + dx, + 0, + &sr, + &sr, + NULL, + NULL, + SW_INVALIDATE | SW_SCROLLCHILDREN); + } + + if(lr.top < lr.bottom && lr.left < lr.right) { + InvalidateRect(hwnd, &lr, FALSE); + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +LRESULT +cw_wm_vscroll(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + khui_credwnd_tbl * tbl; + SCROLLINFO si; + RECT cr; + RECT sr; + RECT lr; + int dy; + int newpos; + + tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + GetClientRect(hwnd, &cr); + cr.top += tbl->header_height; + dy = tbl->scr_top; + + switch(LOWORD(wParam)) { + case SB_LEFT: + newpos = 0; + break; + + case SB_BOTTOM: + newpos = tbl->ext_height; + break; + + case SB_LINEUP: + newpos = tbl->scr_top - (tbl->ext_height / 12); + break; + + case SB_LINEDOWN: + newpos = tbl->scr_top + (tbl->ext_height / 12); + break; + + case SB_PAGEUP: + newpos = tbl->scr_top - (cr.bottom - cr.top); + break; + + case SB_PAGEDOWN: + newpos = tbl->scr_top + (cr.bottom - cr.top); + break; + + case SB_THUMBTRACK: + case SB_THUMBPOSITION: + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_TRACKPOS; + GetScrollInfo(hwnd, SB_VERT, &si); + + newpos = si.nTrackPos; + break; + + default: + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + + tbl->scr_top = newpos; + cw_update_extents(tbl, TRUE); + + dy -= tbl->scr_top; + + /* exclude watermark */ + lr.bottom = cr.bottom; + lr.right = cr.right; + lr.top = max(cr.bottom - tbl->kbm_logo_shade.cy, cr.top); + lr.left = max(cr.right - tbl->kbm_logo_shade.cx, cr.left); + + if(cr.left < lr.left && cr.top < cr.bottom) { + sr.left = cr.left; + sr.right = lr.left; + sr.top = cr.top; + sr.bottom = cr.bottom; + ScrollWindowEx( + hwnd, + 0, + dy, + &sr, + &sr, + NULL, + NULL, + SW_INVALIDATE); + } + + if(lr.left < lr.right && cr.top < lr.top) { + sr.left = lr.left; + sr.right = lr.right; + sr.top = cr.top; + sr.bottom = lr.top; + ScrollWindowEx( + hwnd, + 0, + dy, + &sr, + &sr, + NULL, + NULL, + SW_INVALIDATE); + } + + if(lr.top < lr.bottom && lr.left < lr.right) { + InvalidateRect(hwnd, &lr, FALSE); + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +static INT_PTR CALLBACK +cw_pp_ident_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ) +{ + switch(uMsg) { + case WM_INITDIALOG: + { + khui_property_sheet * s; + PROPSHEETPAGE * p; + khm_handle ident; + wchar_t idname[KCDB_IDENT_MAXCCH_NAME]; + khm_size t; + khm_int32 i; + + p = (PROPSHEETPAGE *) lParam; + s = (khui_property_sheet *) p->lParam; + +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) s); +#pragma warning(pop) + + ident = s->identity; + + t = sizeof(idname); + kcdb_identity_get_name(ident, idname, &t); + SetDlgItemText(hwnd, IDC_PP_IDNAME, idname); + + kcdb_identity_get_flags(ident, &i); + + SendDlgItemMessage(hwnd, + IDC_PP_IDDEF, + BM_SETCHECK, + (WPARAM) ((i & KCDB_IDENT_FLAG_DEFAULT)?BST_CHECKED:BST_UNCHECKED), + 0); + + SendDlgItemMessage( + hwnd, + IDC_PP_IDSEARCH, + BM_SETCHECK, + (WPARAM) ((i & KCDB_IDENT_FLAG_SEARCHABLE)?BST_CHECKED:BST_UNCHECKED), + 0); + + khui_property_wnd_set_record(GetDlgItem(hwnd, IDC_PP_PROPLIST), + ident); + } + return TRUE; + } + return FALSE; +} + +static INT_PTR CALLBACK +cw_pp_cred_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ) +{ + switch(uMsg) { + case WM_INITDIALOG: + { + khui_property_sheet * s; + PROPSHEETPAGE * p; + khm_handle cred; + + p = (PROPSHEETPAGE *) lParam; + s = (khui_property_sheet *) p->lParam; + +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR) s); +#pragma warning(pop) + + cred = s->cred; + + khui_property_wnd_set_record( + GetDlgItem(hwnd, IDC_PP_CPROPLIST), + cred); + } + return TRUE; + } + return FALSE; +} + +static void +cw_pp_begin(khui_property_sheet * s) +{ + PROPSHEETPAGE *p; + + if(s->identity) { + p = malloc(sizeof(*p)); + ZeroMemory(p, sizeof(*p)); + + p->dwSize = sizeof(*p); + p->dwFlags = 0; + p->hInstance = khm_hInstance; + p->pszTemplate = MAKEINTRESOURCE(IDD_PP_IDENT); + p->pfnDlgProc = cw_pp_ident_proc; + p->lParam = (LPARAM) s; + khui_ps_add_page(s, KHUI_PPCT_IDENTITY, 0, p, NULL); + } + + if(s->cred) { + p = malloc(sizeof(*p)); + ZeroMemory(p, sizeof(*p)); + + p->dwSize = sizeof(*p); + p->dwFlags = 0; + p->hInstance = khm_hInstance; + p->pszTemplate = MAKEINTRESOURCE(IDD_PP_CRED); + p->pfnDlgProc = cw_pp_cred_proc; + p->lParam = (LPARAM) s; + khui_ps_add_page(s, KHUI_PPCT_CREDENTIAL, 0, p, NULL); + } +} + +static void +cw_pp_precreate(khui_property_sheet * s) +{ + khui_ps_show_sheet(khm_hwnd_main, s); + + khm_add_property_sheet(s); +} + +static void +cw_pp_end(khui_property_sheet * s) +{ + khui_property_page * p = NULL; + + khui_ps_find_page(s, KHUI_PPCT_IDENTITY, &p); + if(p) { + free(p->p_page); + p->p_page = NULL; + } + + p = NULL; + + khui_ps_find_page(s, KHUI_PPCT_CREDENTIAL, &p); + if(p) { + free(p->p_page); + p->p_page = NULL; + } +} + +static void +cw_pp_destroy(khui_property_sheet *ps) +{ + if(ps->ctx.scope == KHUI_SCOPE_CRED) { + if(ps->header.pszCaption) + free((LPWSTR) ps->header.pszCaption); + } + + khui_ps_destroy_sheet(ps); + + /* this is pretty weird because ps gets freed when + khui_ps_destroy_sheet() is called. However, since destroying ps + involves sending a WM_DESTROY message to the property sheet, we + still need to keep it on the property sheet chain (or else the + messages will not be delivered). This is only safe because we are + not relinquishing the thread in-between destroying ps and removing + it from the chain. */ + + /*TODO: fix this */ + khm_del_property_sheet(ps); +} + +LRESULT +cw_properties(HWND hwnd) +{ + /* show a property sheet of some sort */ + khui_action_context ctx; + khui_property_sheet * ps; + khui_credwnd_tbl * tbl; + + khui_context_get(&ctx); + tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + + if(ctx.scope == KHUI_SCOPE_NONE) { + + return FALSE; + + /* While it seems like a good idea, doing this is not */ +#if 0 + /* try to establish a context based on the current cursor + position */ + if(tbl->cursor_row >= 0 && tbl->cursor_row < (int) tbl->n_rows) { + if(tbl->rows[tbl->cursor_row].flags & KHUI_CW_ROW_HEADER) { + if(tbl->cols[tbl->rows[tbl->cursor_row].col].attr_id == KCDB_ATTR_ID_NAME) { + /* identity context */ + ctx.ctx = KHUI_SCOPE_IDENT; + ctx.identity = (khm_handle) + ((khui_credwnd_outline *) tbl->rows[tbl->cursor_row].data)->data; + } else if(tbl->cols[tbl->rows[tbl->cursor_row].col].attr_id == KCDB_ATTR_TYPE_NAME) { + ctx.ctx = KHUI_SCOPE_CREDTYPE; + ctx.cred_type = (khm_int32) (DWORD_PTR) + ((khui_credwnd_outline *) tbl->rows[tbl->cursor_row].data)->data; + } else { + ctx.ctx = KHUI_SCOPE_GROUP; + //ctx.parm = (khm_lparm) tbl->rows[tbl->cursor_row].data; + /* TODO: Figure out method of establishing a credgroup */ + } + } else { + /* a credential context */ + ctx.ctx = KHUI_SCOPE_CRED; + ctx.cred = (khm_handle) tbl->rows[tbl->cursor_row].data; + } + } +#endif + } + + /* if still no context, then we can't show a property sheet */ + if(ctx.scope == KHUI_SCOPE_NONE) { + return FALSE; + } + + khui_ps_create_sheet(&ps); + + if(ctx.scope == KHUI_SCOPE_IDENT) { + khm_handle ident; + khm_size t; + + ident = ctx.identity; + + ps->header.hInstance = khm_hInstance; + ps->header.pszIcon = MAKEINTRESOURCE(IDI_MAIN_APP); + + kcdb_identity_get_name(ident, NULL, &t); + + if(t > 0) { + ps->header.pszCaption = malloc(t); + kcdb_identity_get_name(ident, (wchar_t *) ps->header.pszCaption, &t); + } else { + ps->header.pszCaption = NULL; + } + + ps->ctx = ctx; + ps->identity = ident; + ps->credtype = KCDB_CREDTYPE_INVALID; + + kmq_post_message(KMSG_CRED, KMSG_CRED_PP_BEGIN, 0, (void *) ps); + + } else if(ctx.scope == KHUI_SCOPE_CREDTYPE) { + khm_size t = 0; + khm_int32 cred_type; + + cred_type = ctx.cred_type; + + ps->header.hInstance = khm_hInstance; + ps->header.pszIcon = MAKEINTRESOURCE(IDI_MAIN_APP); + + ps->ctx = ctx; + ps->credtype = cred_type; + + if(ctx.identity) { + ps->identity = ctx.identity; + /* also, if there is an associated identity, we assume that + the properties are for the specified credentials type + specific to the identity. Hence we change the title to + something else */ + kcdb_identity_get_name(ctx.identity, NULL, &t); + if (t > 0) { + ps->header.pszCaption = malloc(t); + kcdb_identity_get_name(ctx.identity, (wchar_t *) ps->header.pszCaption, &t); + } else { + ps->header.pszCaption = NULL; + } + } else { + kcdb_credtype_describe(cred_type, NULL, &t, KCDB_TS_LONG); + if(t > 0) { + ps->header.pszCaption = malloc(t); + kcdb_credtype_describe(cred_type, (wchar_t *) ps->header.pszCaption, &t, KCDB_TS_LONG); + } else { + ps->header.pszCaption = NULL; + } + } + + kmq_post_message(KMSG_CRED, KMSG_CRED_PP_BEGIN, 0, (void *) ps); + } else if(ctx.scope == KHUI_SCOPE_CRED) { + khm_handle cred; + khm_size t; + + cred = ctx.cred; + + ps->header.hInstance = khm_hInstance; + ps->header.pszIcon = MAKEINTRESOURCE(IDI_MAIN_APP); + ps->ctx = ctx; + + kcdb_cred_get_name(cred, NULL, &t); + ps->header.pszCaption = malloc(t); + kcdb_cred_get_name(cred, (LPWSTR) ps->header.pszCaption, &t); + + kcdb_cred_get_identity(cred, &ps->identity); + kcdb_cred_get_type(cred, &ps->credtype); + ps->cred = cred; + + kmq_post_message(KMSG_CRED, KMSG_CRED_PP_BEGIN, 0, (void *) ps); + } else { + khui_ps_destroy_sheet(ps); + } + + khui_context_reset(); + + return TRUE; +} + +LRESULT +cw_wm_command(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + khui_credwnd_tbl * tbl; + + tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + + if(HIWORD(wParam) == BN_CLICKED && + LOWORD(wParam) == KHUI_HTWND_CTLID) { + + wchar_t wid[256]; + /* a hyperlink was activated */ + khui_htwnd_link * l; + l = (khui_htwnd_link *) lParam; + wcsncpy(wid, l->id, l->id_len); + wid[l->id_len] = 0; + + if(!wcscmp(wid, L"NewCreds")) { + PostMessage(khm_hwnd_main, WM_COMMAND, + MAKEWPARAM(KHUI_ACTION_NEW_CRED,0), 0); + } + return TRUE; + } + + switch(LOWORD(wParam)) + { + case KHUI_PACTION_ENTER: + /* enter key is a synonym for the default action, on the + context, which is to lauch a property sheet */ + /* fallthrough */ + case KHUI_ACTION_PROPERTIES: + { + return cw_properties(hwnd); + } + break; + + case KHUI_ACTION_LAYOUT_ID: + { + cw_unload_view(tbl); + + cw_load_view(tbl, L"ByIdentity", hwnd); + cw_insert_header_cols(tbl); + + cw_update_creds(tbl); + cw_update_outline(tbl); + cw_update_extents(tbl, FALSE); + + InvalidateRect(tbl->hwnd, NULL, TRUE); + + khui_check_radio_action(khui_find_menu(KHUI_MENU_LAYOUT), KHUI_ACTION_LAYOUT_ID); + } + break; + + case KHUI_ACTION_LAYOUT_LOC: + { + cw_unload_view(tbl); + + cw_load_view(tbl, L"ByLocation", hwnd); + cw_insert_header_cols(tbl); + + cw_update_creds(tbl); + cw_update_outline(tbl); + cw_update_extents(tbl, FALSE); + + InvalidateRect(tbl->hwnd, NULL, TRUE); + + khui_check_radio_action(khui_find_menu(KHUI_MENU_LAYOUT), + KHUI_ACTION_LAYOUT_LOC); + } + break; + + case KHUI_PACTION_UP: + case KHUI_PACTION_UP_EXTEND: + case KHUI_PACTION_UP_TOGGLE: + { /* cursor up */ + khm_int32 new_row; + WPARAM wp; + + new_row = tbl->cursor_row - 1; + + /* checking both bounds. we make no assumption about the + value of cursor_row before this message */ + if(new_row < 0) + new_row = 0; + if(new_row >= (int) tbl->n_rows) + new_row = (int) tbl->n_rows; + + if (LOWORD(wParam) == KHUI_PACTION_UP) + wp = 0; + else if (LOWORD(wParam) == KHUI_PACTION_UP_EXTEND) + wp = MK_SHIFT; + else if (LOWORD(wParam) == KHUI_PACTION_UP_TOGGLE) + wp = 0; //MK_CONTROL; +#ifdef DEBUG + else + assert(FALSE); +#endif + + cw_select_row(tbl, new_row, wp); + } + break; + + case KHUI_PACTION_DOWN: + case KHUI_PACTION_DOWN_EXTEND: + case KHUI_PACTION_DOWN_TOGGLE: + { /* cursor down */ + khm_int32 new_row; + WPARAM wp; + + new_row = tbl->cursor_row + 1; + + /* checking both bounds. we make no assumption about the + value of cursor_row before this message */ + if(new_row < 0) + new_row = 0; + if(new_row >= (int) tbl->n_rows) + new_row = (int) tbl->n_rows; + + if (LOWORD(wParam) == KHUI_PACTION_DOWN) + wp = 0; + else if (LOWORD(wParam) == KHUI_PACTION_DOWN_EXTEND) + wp = MK_SHIFT; + else if (LOWORD(wParam) == KHUI_PACTION_DOWN_TOGGLE) + wp = 0; //MK_CONTROL; +#ifdef DEBUG + else + assert(FALSE); +#endif + cw_select_row(tbl, new_row, wp); + } + break; + + case KHUI_PACTION_LEFT: + { /* collapse and up*/ + khui_credwnd_outline * o; + int r; + + if(tbl->cursor_row < 0 || tbl->cursor_row >= (int) tbl->n_rows) { + cw_select_row(tbl, 0, 0); + break; + } + + for(r = tbl->cursor_row; + (r >= 0 && !(tbl->rows[r].flags & KHUI_CW_ROW_HEADER)); + r--); + + if(r < 0) + break; + + /* If we were not on a header, we collapse the innermost + outline. Otherwise, we collpase up to the parent + outline level */ + + if(r != tbl->cursor_row) { + o = (khui_credwnd_outline *) tbl->rows[r].data; + + cw_toggle_outline_state(tbl, o); + } else { + o = (khui_credwnd_outline *) tbl->rows[r].data; + + if(o->flags & KHUI_CW_O_EXPAND) { + cw_toggle_outline_state(tbl, o); + } else { + o = TPARENT(o); + if(o) { + cw_toggle_outline_state(tbl, o); + r = o->start; + } else if(r > 0) + r--; + } + } + + cw_select_row(tbl, r, 0); + } + break; + + case KHUI_PACTION_RIGHT: + { /* expand and down*/ + khui_credwnd_outline * o; + int r; + + if(tbl->cursor_row < 0 || + tbl->cursor_row >= (int) tbl->n_rows) { + cw_select_row(tbl, 0, 0); + break; + } + + r = tbl->cursor_row; + + if(tbl->rows[r].flags & KHUI_CW_ROW_HEADER) { + o = (khui_credwnd_outline *) tbl->rows[r].data; + if(!(o->flags & KHUI_CW_O_EXPAND)) { + cw_toggle_outline_state(tbl, o); + } + } + + r++; + + cw_select_row(tbl, r, 0); + } + break; + } + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +LRESULT +cw_wm_contextmenu(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + RECT r; + int x,y; + int row; + khui_credwnd_tbl * tbl; + + tbl = (khui_credwnd_tbl *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + + GetWindowRect(hwnd, &r); + + x = GET_X_LPARAM(lParam); + y = GET_Y_LPARAM(lParam); + + x += tbl->scr_left - r.left; + y += tbl->scr_top - tbl->header_height - r.top; + + row = y / tbl->cell_height; + + if(row < 0 || row >= (int) tbl->n_rows) + return FALSE; + + cw_set_row_context(tbl, row); + + if((tbl->rows[row].flags & KHUI_CW_ROW_HEADER) && + (tbl->cols[tbl->rows[row].col].attr_id == KCDB_ATTR_ID_NAME)) + { + khm_menu_show_panel(KHUI_MENU_IDENT_CTX, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + //khui_context_reset(); + } else { + khm_menu_show_panel(KHUI_MENU_TOK_CTX, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + //khui_context_reset(); + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +/* copy and paste template */ +#if 0 +LRESULT +cw_wm_msg(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} +#endif + +LRESULT CALLBACK +khm_credwnd_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + switch(uMsg) { + case WM_COMMAND: + return cw_wm_command(hwnd, uMsg, wParam, lParam); + + case WM_CREATE: + return cw_wm_create(hwnd, uMsg, wParam, lParam); + + case WM_DESTROY: + return cw_wm_destroy(hwnd, uMsg, wParam, lParam); + + case WM_ERASEBKGND: + /* we don't bother wasting cycles erasing the background + because the foreground elements completely cover the + client area */ + return TRUE; + + case WM_PAINT: + return cw_wm_paint(hwnd, uMsg, wParam, lParam); + + case WM_SIZE: + return cw_wm_size(hwnd, uMsg, wParam, lParam); + + case WM_NOTIFY: + return cw_wm_notify(hwnd, uMsg, wParam, lParam); + + case WM_HSCROLL: + return cw_wm_hscroll(hwnd, uMsg, wParam, lParam); + + case WM_VSCROLL: + return cw_wm_vscroll(hwnd, uMsg, wParam, lParam); + + case KMQ_WM_DISPATCH: + return cw_kmq_wm_dispatch(hwnd, uMsg, wParam, lParam); + + case WM_LBUTTONDBLCLK: + case WM_LBUTTONDOWN: + case WM_MOUSEMOVE: + case WM_LBUTTONUP: + return cw_wm_mouse(hwnd, uMsg, wParam, lParam); + + case WM_CONTEXTMENU: + return cw_wm_contextmenu(hwnd, uMsg, wParam, lParam); + } + + return DefWindowProc(hwnd,uMsg,wParam,lParam); +} + +void +khm_register_credwnd_class(void) { + WNDCLASSEX wcx; + kcdb_attrib attrib; + khm_int32 attr_id; + + wcx.cbSize = sizeof(wcx); + wcx.style = CS_DBLCLKS | CS_OWNDC; + wcx.lpfnWndProc = khm_credwnd_proc; + wcx.cbClsExtra = 0; + wcx.cbWndExtra = sizeof(LONG_PTR); + wcx.hInstance = khm_hInstance; + wcx.hIcon = NULL; + wcx.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW); + wcx.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1); + wcx.lpszMenuName = NULL; + wcx.lpszClassName = KHUI_CREDWND_CLASS_NAME; + wcx.hIconSm = NULL; + + khui_credwnd_cls = RegisterClassEx(&wcx); + + /* while we are at it, register the credwnd attribute type as well, and + obtain the type ID */ + if(KHM_FAILED(kcdb_attrib_get_id(KHUI_CREDWND_FLAG_ATTRNAME, &attr_id))) { + ZeroMemory(&attrib, sizeof(attrib)); + attrib.id = KCDB_ATTR_INVALID; + attrib.flags = KCDB_ATTR_FLAG_HIDDEN; + attrib.type = KCDB_TYPE_INT32; + attrib.name = KHUI_CREDWND_FLAG_ATTRNAME; + + kcdb_attrib_register(&attrib, &attr_id); + } + + khui_cw_flag_id = attr_id; +} + +void +khm_unregister_credwnd_class(void) { + UnregisterClass(MAKEINTATOM(khui_credwnd_cls), khm_hInstance); +} + +HWND +khm_create_credwnd(HWND parent) { + RECT r; + GetClientRect(parent, &r); + return CreateWindowEx( + 0, + MAKEINTATOM(khui_credwnd_cls), + L"", + WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL, + r.left, + r.top, + r.right - r.left, + r.bottom - r.top, + parent, + NULL, + khm_hInstance, + NULL); +} diff --git a/src/windows/identity/ui/credwnd.h b/src/windows/identity/ui/credwnd.h new file mode 100644 index 000000000..66fc3d2fb --- /dev/null +++ b/src/windows/identity/ui/credwnd.h @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_CREDWND_H +#define __KHIMAIRA_CREDWND_H + +#define KHUI_CREDWND_CLASS_NAME L"NetIDMgrCredWnd" + +#define KHUI_CREDWND_FLAG_ATTRNAME L"CredWndFlags" + +extern khm_int32 khui_cw_flag_id; + +/* The expiration states */ +#define CW_EXPSTATE_NONE 0 +#define CW_EXPSTATE_WARN 1024 +#define CW_EXPSTATE_CRITICAL 2048 +#define CW_EXPSTATE_EXPIRED 3072 + +#define CW_EXPSTATE_MASK 3072 + +typedef struct khui_credwnd_outline_t { + khm_int32 flags; /* combination of KHUI_CW_O_* */ + khm_int32 start; /* first row of outline */ + khm_int32 length; /* number of rows in outline */ + khm_int32 level; /* outline level */ + khm_int32 col; /* outline column */ + wchar_t *header; /* character string associated with header */ + khm_int32 attr_id; + void * data; /* level specific data : + Identity -> handle to identity + Type -> type ID + otherwise -> canonical data buffer + */ + khm_size cb_data; + + khm_size idx_start; /* index of the first cred in the credset */ + khm_size idx_end; /* index of the last cred in the credset */ + TDCL(struct khui_credwnd_outline_t); +} khui_credwnd_outline; + +#define KHUI_CW_O_EXPAND 0x00000001 +#define KHUI_CW_O_STICKY 0x00000002 +#define KHUI_CW_O_VISIBLE 0x00000004 +#define KHUI_CW_O_SHOWFLAG 0x00000008 +#define KHUI_CW_O_SELECTED 0x00000010 +#define KHUI_CW_O_DATAALLOC 0x00000020 + +typedef struct khui_credwnd_row_t { + khm_int32 flags; + khm_int32 col; + khm_handle data; + khm_size idx_start; + khm_size idx_end; +} khui_credwnd_row; + +#define KHUI_CW_ROW_CRED 2 +#define KHUI_CW_ROW_HEADER 4 +#define KHUI_CW_ROW_TIMERSET 8 +#define KHUI_CW_ROW_SELECTED 16 + +/* row allocation */ +/* initial number of rows to be allocated */ +#define KHUI_CW_ROW_INITIAL 512 +/* allocation increment, if we run out of space */ +#define KHUI_CW_ROW_INCREMENT 512 + +typedef struct khui_credwnd_col_t { + khm_int32 attr_id; + khm_int32 width; /* width of the column (screen units) */ + khm_int32 x; /* starting x coordinate (screen units) */ + khm_int32 flags; /* combination of KHUI_CW_COL_* */ + khm_int32 sort_index; + wchar_t * title; +} khui_credwnd_col; + +/* column allocation */ +/* initial number of columns to be allocated */ +#define KHUI_CW_COL_INITIAL 16 +/* allocation increment, if we run out of space */ +#define KHUI_CW_COL_INCREMENT 16 + +#define KHUI_CW_COL_AUTOSIZE 1 +#define KHUI_CW_COL_SORT_INC 2 +#define KHUI_CW_COL_SORT_DEC 4 +#define KHUI_CW_COL_GROUP 8 +#define KHUI_CW_COL_FIXED_WIDTH 16 +#define KHUI_CW_COL_FIXED_POS 32 +#define KHUI_CW_COL_META 64 + +/* Custom column attributes (are not kcdb attributes) */ +#define CW_CA_FLAGS -1 +#define CW_CANAME_FLAGS L"_CWFlags" + +#define CW_CA_TYPEICON -2 +#define CW_CANAME_TYPEICON L"_CWTypeIcon" + +#define cw_is_custom_attr(i) ((i)<0) + +typedef struct khui_credwnd_tbl_t { + HWND hwnd; /* the window that this table belongs to */ + + khm_int32 scr_top; /* screen units */ + khm_int32 scr_left; /* screen units */ + khm_int32 ext_width; /* screen units */ + khm_int32 ext_height; /* screen units */ + khm_int32 cell_height; /* screen units */ + + HWND hwnd_header; /* header control */ + khm_int32 header_height; /* height of the header */ + HWND hwnd_notif; /* notification control */ + + khui_credwnd_col * cols; /* n_cols elements */ + khui_credwnd_row * rows; /* n_rows elements */ + khm_size n_cols; + khm_size n_total_cols; /* number of columns actually + allocated in cols */ + khm_size n_rows; + khm_size n_total_rows; /* number of rows actually allocated + in rows */ + + khui_credwnd_outline * outline; + + khm_int32 flags; /* combo of KHUI_CW_TBL_* */ + + khm_int32 cursor_row; /* cursor and selection */ + khm_int32 anchor_row; /* anchor, for range selections */ + + /* view parameters */ + khm_int32 hpad; + khm_int32 vpad; + khm_int32 hpad_h; /* horizontal padding correction for headers */ + khm_int32 threshold_warn; /* Warning threshold, in seconds*/ + khm_int32 threshold_critical; /* Critical threshold, in seconds */ + + /* graphics objects we are going to need. */ + HFONT hf_normal; /* normal text */ + HFONT hf_header; /* header text */ + HFONT hf_bold; /* bold text */ + HFONT hf_bold_header; /* bold header text */ + HBRUSH hb_normal; /* normal background brush */ + HBRUSH hb_grey; /* normal grey background brush */ + HBRUSH hb_sel; /* selected background brush */ + COLORREF cr_hdr_outline;/* header outline color */ + COLORREF cr_normal; /* normal text color */ + COLORREF cr_sel; /* selected text color */ + COLORREF cr_hdr_normal; /* normal header text color */ + COLORREF cr_hdr_sel; /* selected header text color */ + HBRUSH hb_hdr_bg; /* header background color (normal) */ + HBRUSH hb_hdr_bg_exp; /* header background color (expired) */ + HBRUSH hb_hdr_bg_warn; /* header background color (warn) */ + HBRUSH hb_hdr_bg_crit; /* header background color (critical) */ + HBRUSH hb_hdr_bg_sel; /* header background color (selected) */ + HCURSOR hc_hand; /* the HAND cursor */ + khui_ilist * ilist; /* image list */ + +#if 0 + /* icon indices */ + int idx_expand; /* index of 'expanded' icon in image list */ + int idx_expand_hi; /* index of 'expanded' icon (highlighted) in image list */ + int idx_collapse; /* index of 'collapsed' icon in image list */ + int idx_collapse_hi; /* index of 'collapsed' icon (highlighted) in image list */ + int idx_ident; /* index of 'identity' icon in image list */ +#endif + + /* mouse state */ + khm_int32 mouse_state; /* state of the mouse can be combo of CW_MOUSE_* values */ + khm_int32 mouse_row; /* row that the mouse state applies to */ + khm_int32 mouse_col; /* col that the mouse state applies to */ + + khui_bitmap kbm_logo_shade; + + /* the credentials set */ + khm_handle credset; +} khui_credwnd_tbl; + +#define KHUI_MAXCB_HEADING 256 + +/* table flags */ +#define KHUI_CW_TBL_INITIALIZED 0x00000001 +#define KHUI_CW_TBL_COL_DIRTY 0x00000002 +#define KHUI_CW_TBL_ROW_DIRTY 0x00000004 +#define KHUI_CW_TBL_ACTIVE 0x00000100 + +/* mouse_state constants */ +#define CW_MOUSE_NONE 0 /* nothing interesting */ +#define CW_MOUSE_OUTLINE 1 /* mouse is highlighting an outline widget */ +#define CW_MOUSE_LDOWN 2 /* left button is down */ +#define CW_MOUSE_ROW 4 /* mouse is acive over a valid row */ + +void khm_unregister_credwnd_class(void); + +void khm_register_credwnd_class(void); + +HWND khm_create_credwnd(HWND parent); + +LRESULT CALLBACK khm_credwnd_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ); + +void cw_load_view(khui_credwnd_tbl * tbl, wchar_t * viewname, HWND hwnd); + +void cw_update_creds(khui_credwnd_tbl * tbl); + +void cw_unload_view(khui_credwnd_tbl * tbl); + +void cw_hditem_from_tbl_col(khui_credwnd_col * col, HDITEM *phi); + +int cw_update_extents(khui_credwnd_tbl * tbl, khm_boolean update_scroll); + +void cw_insert_header_cols(khui_credwnd_tbl * tbl); + +#endif diff --git a/src/windows/identity/ui/htmlwnd.h b/src/windows/identity/ui/htmlwnd.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/windows/identity/ui/htwnd.c b/src/windows/identity/ui/htwnd.c new file mode 100644 index 000000000..9acbac704 --- /dev/null +++ b/src/windows/identity/ui/htwnd.c @@ -0,0 +1,1070 @@ +/* +* Copyright (c) 2004 Massachusetts Institute of Technology +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, +* modify, merge, publish, distribute, sublicense, and/or sell copies +* of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +/* $Id$ */ + +#include<khmapp.h> +#include<crtdbg.h> + +ATOM khui_htwnd_cls; + +#define HTW_STYLE_NORMAL 0 + +/* There are currently 4 style "bits" and 3 sizes, which means + there can be 2^4*3=48 possible styles max. If someone is + feeling adventurous you can slightly improve performance of + the parser using this little fact. For now, I don't care. + (hint: combine size and style bits to form a single number + and use it as an index into the styles array) +*/ +#define HTW_STYLE_MAX 48 + +#define HTW_FORMAT_MAX 128 + +#define HTW_TAB_MAX 8 + +#define HTW_DEFAULT (-1) + +#define HTW_NORMAL_SIZE 8 +#define HTW_LARGE_SIZE 12 +#define HTW_HUGE_SIZE 20 + +/* font variant */ +#define FV_ABSOLUTE 0x10000000 + +#define FV_ITALIC 0x00000002 +#define FV_UNDERLINE 0x00000004 +#define FV_STRIKEOUT 0x00000008 +#define FV_BOLD 0x00000010 + +#define FV_NOITALIC 0x00020000 +#define FV_NOUNDERLINE 0x00040000 +#define FV_NOSTRIKEOUT 0x00080000 +#define FV_NOBOLD 0x00100000 + +#define FV_NONE 0x00000000 +#define FV_MASK 0x0000001f + +#define HTW_LINK_ALLOC 8 + +#define ALIGN_LEFT 0 +#define ALIGN_CENTER 1 +#define ALIGN_RIGHT 2 + +struct tx_tbl_t { + wchar_t * string; + LONG value; +} + +htw_color_table[] = { + {L"black", RGB(0,0,0)}, + {L"white", RGB(255,255,255)}, + {L"red", RGB(255,0,0)}, + {L"green", RGB(0,255,0)}, + {L"blue", RGB(0,0,255)}, + {L"grey", RGB(128,128,128)} +}, + +htw_size_table[] = { + {L"normal", HTW_NORMAL_SIZE}, + {L"large", HTW_LARGE_SIZE}, + {L"huge", HTW_HUGE_SIZE} +}, + +htw_align_table[] = { + {L"left", ALIGN_LEFT}, + {L"center", ALIGN_LEFT}, + {L"right", ALIGN_RIGHT} +}; + +typedef struct khui_htwnd_style_t { + LONG height; + LONG variation; /* combination of FV_* */ + + HFONT font; +} khui_htwnd_style; + +typedef struct khui_format_t { + int style_idx; + COLORREF color; +} khui_format; + +typedef struct format_stack_t { + khui_format stack[HTW_FORMAT_MAX]; + int stack_top; +} format_stack; + +typedef struct khui_htwnd_data_t { + int id; /* control ID */ + int flags; + wchar_t * text; + int scroll_left; + int scroll_top; + COLORREF bk_color; + HCURSOR hc_hand; + int l_pixel_y; + + khui_htwnd_style styles[HTW_STYLE_MAX]; + int n_styles; + + khui_htwnd_link ** links; + int n_links; + int max_links; + int active_link; + int md_link; + + int tabs[HTW_TAB_MAX]; + int n_tabs; +} khui_htwnd_data; + +static LONG table_lookup(struct tx_tbl_t * tbl, int n, wchar_t * v, int len) +{ + int i; + + for(i=0; i<n; i++) { + if(!wcsnicmp(tbl[i].string, v, len)) + return tbl[i].value; + } + + return -1; +} + +static void clear_styles(khui_htwnd_data * d) +{ + int i; + + for(i=0; i<d->n_styles; i++) { + if(d->styles[i].font != NULL) { + DeleteObject(d->styles[i].font); + d->styles[i].font = NULL; + } + } + + d->n_styles = 0; +} + +static void format_init(format_stack * s) +{ + s->stack_top = -1; + ZeroMemory(s->stack, sizeof(s->stack)); +} + +static khui_format * format_current(format_stack * s) +{ + if(s->stack_top >= 0) + return &(s->stack[s->stack_top]); + else + return NULL; +} + +static int format_style(format_stack * s) +{ + if(s->stack_top >= 0) + return s->stack[s->stack_top].style_idx; + else + return 0; +} + +static COLORREF format_color(format_stack * s) +{ + if(s->stack_top >= 0) + return s->stack[s->stack_top].color; + else + return 0; +} + +static int format_level(format_stack * s) +{ + return s->stack_top; +} + +static void format_unwind(format_stack * s, int level) +{ + s->stack_top = level; +} + +static void format_push(format_stack * s, khui_htwnd_data * d, LONG height, LONG variation, COLORREF color) +{ + int i; + khui_format * top; + khui_htwnd_style * style; + + _ASSERT(s->stack_top < (HTW_FORMAT_MAX-1)); + + /* formatting is additive unless FV_NORMAL is set in variation */ + top = format_current(s); + if(top) { + style = &(d->styles[top->style_idx]); + if(height == HTW_DEFAULT) + height = style->height; + + if(variation == HTW_DEFAULT) + variation = style->variation; + else if(!(variation & FV_ABSOLUTE)) + variation |= style->variation; + + if(color == HTW_DEFAULT) + color = top->color; + } + + variation &= ~FV_ABSOLUTE; + variation ^= variation & (variation>>16); + variation &= FV_MASK; + + /* now look for an existing style that matches the requested one */ + for(i=0; i<d->n_styles; i++) { + style = &(d->styles[i]); + + if(style->height == height && + style->variation == variation) + break; + } + + s->stack_top++; + + if(i<d->n_styles) { + s->stack[s->stack_top].style_idx = i; + } else { + if(d->n_styles == HTW_STYLE_MAX) { + s->stack[s->stack_top].style_idx = 0; + } else { + s->stack[s->stack_top].style_idx = d->n_styles; + d->styles[d->n_styles].font = NULL; + d->styles[d->n_styles].height = height; + d->styles[d->n_styles].variation = variation; + d->n_styles++; + } + } + s->stack[s->stack_top].color = color; +} + +static void format_pop(format_stack * s) { + if(s->stack_top >= 0) + s->stack_top--; +} + +static wchar_t * token_end(wchar_t * s) { + while(iswalnum(*s) || *s == L'/') + s++; + return s; +} + +static wchar_t * skip_ws(wchar_t * s) { + while(iswspace(*s)) + s++; + return s; +} + +/* s points to something like " = \"value\"" + start and len will point to the start and + length of value. return value will point to the + character following the last double quote. */ +static wchar_t * read_attr(wchar_t * s, wchar_t ** start, int * len) +{ + wchar_t *e; + + *start = NULL; + *len = 0; + + do { + s = skip_ws(s); + if(*s != L'=') + break; + s = skip_ws(++s); + if(*s != L'"') + break; + e = wcschr(++s, L'"'); + if(!e) + break; + + *start = s; + *len = (int) (e - s); + + s = e + 1; + } while(FALSE); + + return s; +} + +/* +We currently support the following tags: + +<a [id="string"] [param="paramstring"]>link text</a> +<b>foo</b> +<u>foo</u> + +<center>foo</center> +<left>foo</left> +<right>foo</right> + +<font [color="color"] [size="normal|large|huge"]>foo</font> +<large>foo</large> +<huge>foo</huge> + +<p [align="left|center|right"]>foo</p> +<settab pos=""> +<tab> +*/ + +static int htw_parse_tag( + wchar_t * start, + wchar_t ** end, + int * align, + khui_htwnd_data * d, + format_stack * s, + PPOINT p_abs, + PPOINT p_rel, + int lh, + BOOL dry_run) +{ + wchar_t * c; + int n = 0; + + /* start initially points to the starting '<' */ + c = token_end(++start); + + if(!wcsnicmp(start,L"a",c-start)) { + /* start of an 'a' tag */ + wchar_t * id_start = NULL; + int id_len = 0; + wchar_t * param_start = NULL; + int param_len = 0; + + /* We don't need to parse the link + if it is just a dry run */ + if(dry_run) { + format_push(s, d, HTW_DEFAULT, HTW_DEFAULT, RGB(0,0,255)); + *end = wcschr(start, L'>'); + return FALSE; + } + + while(c && *c && *c != L'>') { + wchar_t * e; + + c = skip_ws(c); + e = token_end(c); + + if(c==e) + break; + + if(!wcsnicmp(c,L"id",e-c)) { + c = read_attr(e, &id_start, &id_len); + } else if(!wcsnicmp(c,L"param",e-c)) { + c = read_attr(e, ¶m_start, ¶m_len); + } + } + + if(d->active_link == d->n_links) + format_push(s,d, HTW_DEFAULT, FV_UNDERLINE, RGB(0,0,255)); + else + format_push(s,d, HTW_DEFAULT, FV_NONE, RGB(0,0,255)); + + { + khui_htwnd_link * l; + + if(!d->links) { + d->links = malloc(sizeof(khui_htwnd_link *) * HTW_LINK_ALLOC); + ZeroMemory(d->links, sizeof(khui_htwnd_link *) * HTW_LINK_ALLOC); + d->max_links = HTW_LINK_ALLOC; + d->n_links = 0; + } + + if(d->n_links >= d->max_links) { + khui_htwnd_link ** ll; + int n_new; + + n_new = UBOUNDSS(d->n_links + 1, HTW_LINK_ALLOC, HTW_LINK_ALLOC); + + ll = malloc(sizeof(khui_htwnd_link *) * n_new); + ZeroMemory(ll, sizeof(khui_htwnd_link *) * n_new); + memcpy(ll, d->links, sizeof(khui_htwnd_link *) * d->max_links); + free(d->links); + d->links = ll; + d->max_links = n_new; + } + + l = d->links[d->n_links]; + if(!l) { + l = malloc(sizeof(khui_htwnd_link)); + d->links[d->n_links] = l; + } + + l->id = id_start; + l->id_len = id_len; + l->param = param_start; + l->param_len = param_len; + + l->r.left = p_abs->x; + l->r.top = p_abs->y; + + d->n_links++; + } + + } else if(!wcsnicmp(start, L"/a", c - start)) { + khui_htwnd_link * l; + + c = wcschr(c,L'>'); + if(!c) + c = c + wcslen(c); + + format_pop(s); + + if(!dry_run) { + l = d->links[d->n_links - 1]; /* last link */ + l->r.right = p_abs->x; + l->r.bottom = p_abs->y + lh; + } + } else if(!wcsnicmp(start, L"p", c - start)) { + wchar_t * e; + wchar_t * align_s = NULL; + int align_len; + + c = skip_ws(c); + e = token_end(c); + + if(c != e && !wcsnicmp(c,L"align",e-c)) { + c = read_attr(e, &align_s, &align_len); + } + + c = wcschr(c, L'>'); + if(!c) + c = c + wcslen(c); + + + if(align_s) + *align = table_lookup(htw_align_table, ARRAYLENGTH(htw_align_table), align_s, align_len); + else + *align = ALIGN_LEFT; + + n = 1; + } else if(!wcsnicmp(start, L"b", c - start)) { + format_push(s,d, HTW_DEFAULT, FV_BOLD, HTW_DEFAULT); + } else if(!wcsnicmp(start, L"/b", c - start)) { + format_pop(s); + } else if(!wcsnicmp(start, L"u", c - start)) { + format_push(s,d, HTW_DEFAULT, FV_UNDERLINE, HTW_DEFAULT); + } else if(!wcsnicmp(start, L"/u", c - start)) { + format_pop(s); + } else if(!wcsnicmp(start, L"large", c - start)) { + format_push(s,d,-MulDiv(HTW_LARGE_SIZE, d->l_pixel_y, 72), HTW_DEFAULT, HTW_DEFAULT); + } else if(!wcsnicmp(start, L"/large", c - start)) { + format_pop(s); + } else if(!wcsnicmp(start, L"huge", c - start)) { + format_push(s,d,-MulDiv(HTW_HUGE_SIZE, d->l_pixel_y, 72), HTW_DEFAULT, HTW_DEFAULT); + } else if(!wcsnicmp(start, L"/huge", c - start)) { + format_pop(s); + } else if(!wcsnicmp(start, L"center", c - start)) { + c = wcschr(c, L'>'); + if(!c) + c = c + wcslen(c); + *align = ALIGN_CENTER; + n = 1; + } else if(!wcsnicmp(start, L"left", c - start) || + !wcsnicmp(start, L"p", c - start)) + { + c = wcschr(c, L'>'); + if(!c) + c = c + wcslen(c); + *align = ALIGN_LEFT; + n = 1; + } else if(!wcsnicmp(start, L"right", c - start)) { + c = wcschr(c, L'>'); + if(!c) + c = c + wcslen(c); + *align = ALIGN_RIGHT; + n = 1; + } else if(!wcsnicmp(start, L"/center", c - start) || + !wcsnicmp(start, L"/left", c - start) || + !wcsnicmp(start, L"/right", c - start) || + !wcsnicmp(start, L"/p", c - start)) + { + c = wcschr(c, L'>'); + if(!c) + c = c + wcslen(c); + *align = ALIGN_LEFT; + n = 1; + } else if(!wcsnicmp(start, L"font", c - start)) { + wchar_t * color_s = NULL; + int color_len; + wchar_t * size_s = NULL; + int size_len; + LONG color = HTW_DEFAULT; + LONG h = HTW_DEFAULT; + + while(c && *c && *c != L'>') { + wchar_t * e; + + c = skip_ws(c); + e = token_end(c); + + if(c==e) + break; + + if(!wcsnicmp(c,L"color",e-c)) { + c = read_attr(e, &color_s, &color_len); + } else if(!wcsnicmp(c,L"size",e-c)) { + c = read_attr(e, &size_s, &size_len); + } + } + + if(color_s) + color = table_lookup(htw_color_table, ARRAYLENGTH(htw_color_table), color_s, color_len); + if(size_s) { + h = table_lookup(htw_size_table, ARRAYLENGTH(htw_size_table), size_s, size_len); + if(h) + h = -MulDiv(h, d->l_pixel_y, 72); + else + h = -MulDiv(HTW_NORMAL_SIZE, d->l_pixel_y, 72); + } + + format_push(s,d,h,HTW_DEFAULT,color); + } else if(!wcsnicmp(start, L"/font", c - start)) { + format_pop(s); + } else if(!wcsnicmp(start, L"settab", c - start)) { + wchar_t * e; + wchar_t * pos_s = NULL; + int pos_len; + + c = skip_ws(c); + e = token_end(c); + + if(c != e && !wcsnicmp(c,L"pos",e-c)) { + c = read_attr(e, &pos_s, &pos_len); + } + + c = wcschr(c, L'>'); + if(!c) + c = c + wcslen(c); + + if(pos_s && d->n_tabs < HTW_TAB_MAX && !dry_run) { + wchar_t * dummy; + LONG bu; + int bx; + int dx; + + bu = GetDialogBaseUnits(); + bx = LOWORD(bu); + + dx = wcstol(pos_s, &dummy, 10); + + d->tabs[d->n_tabs++] = MulDiv(dx, bx, 4); + } + } else if(!wcsnicmp(start, L"tab", c - start)) { + int i; + + if(!dry_run) { + for(i=0; i < d->n_tabs; i++) { + if(d->tabs[i] > p_rel->x) { + p_rel->x = d->tabs[i]; + break; + } + } + } + } + + if(*c) + c++; + *end = c; + + return n; +} + +static void htw_assert_style(HDC hdc, khui_htwnd_data * d, int style) +{ + LOGFONT lf; + + if(d->styles[style].font) + return; + + /*TODO: we need select different fonts depending on system locale */ + lf.lfHeight = d->styles[style].height; //-MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72); + lf.lfWidth = 0; + lf.lfEscapement = 0; + lf.lfOrientation = 0; + lf.lfWeight = (d->styles[style].variation & FV_BOLD)? FW_BOLD: FW_NORMAL; + lf.lfItalic = !!(d->styles[style].variation & FV_ITALIC); + lf.lfUnderline = !!(d->styles[style].variation & FV_UNDERLINE); + lf.lfStrikeOut = !!(d->styles[style].variation & FV_STRIKEOUT); + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfOutPrecision = OUT_DEFAULT_PRECIS; + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf.lfQuality = DEFAULT_QUALITY; + lf.lfPitchAndFamily = DEFAULT_PITCH; + + LoadString(khm_hInstance, IDS_DEFAULT_FONT, lf.lfFaceName, ARRAYLENGTH(lf.lfFaceName)); + + d->styles[style].font = CreateFontIndirect(&lf); +} + +static LRESULT htw_paint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + PAINTSTRUCT ps; + HBRUSH hbk; + khui_htwnd_data * d; + RECT r; + SIZE s; + HDC hdc; + wchar_t * text; + format_stack s_stack; + + int align; + int y; + wchar_t * par_start; + + d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + + if(!GetUpdateRect(hwnd, &r, !(d->flags & KHUI_HTWND_TRANSPARENT))) + return 0; + + if(d->text == NULL) + return 0; + + text = d->text; + + hdc = BeginPaint(hwnd, &ps); + + GetClientRect(hwnd, &r); + + if(d->flags & KHUI_HTWND_CLIENTEDGE) + DrawEdge(hdc, &r, EDGE_SUNKEN, BF_ADJUST | BF_RECT | BF_FLAT); + + hbk = CreateSolidBrush(RGB(255,255,255)); + FillRect(hdc, &r, hbk); + DeleteObject(hbk); + + /* push the default format */ + format_init(&s_stack); + + d->l_pixel_y = GetDeviceCaps(hdc, LOGPIXELSY); + format_push(&s_stack,d, -MulDiv(HTW_NORMAL_SIZE, d->l_pixel_y, 72), FV_NONE, RGB(0,0,0)); + + y = d->scroll_top + r.top; + + par_start = text; + + align = ALIGN_LEFT; + + SetTextAlign(hdc, TA_TOP | TA_LEFT | TA_NOUPDATECP); + if(d->flags & KHUI_HTWND_TRANSPARENT) + SetBkMode(hdc, TRANSPARENT); + + d->n_links = 0; + d->n_tabs = 0; + + while(*par_start) { + wchar_t * p = par_start; + wchar_t * c = NULL; + int p_width = 0; + int s_start; + int l_height = 0; + int x = 0; + POINT pt; + POINT pt_rel; + + s_start = format_level(&s_stack); + + /* begin dry run */ + while(*p) { + if(*p == L'<') { + int talign = -1; + int n = htw_parse_tag(p,&c,&talign,d,&s_stack,NULL,NULL,0,TRUE); + + if(n && p_width) + break; + + p = c; + + if(n && talign >= 0) + align = talign; + } else { + HFONT hfold; + c = wcschr(p, L'<'); + if(!c) + c = p + wcslen(p); + + htw_assert_style(hdc, d, format_style(&s_stack)); + hfold = SelectFont(hdc, d->styles[format_style(&s_stack)].font); + GetTextExtentPoint32(hdc, p, (int)(c - p), &s); + SelectFont(hdc, hfold); + + p_width += s.cx; + if(s.cy > l_height) + l_height = s.cy; + + p = c; + } + } + + /* dry run ends */ + + x = r.left - d->scroll_left; + + if(align == ALIGN_CENTER) + x += (r.right - r.left)/2 - p_width / 2; + else if(align == ALIGN_RIGHT) + x += (r.right - r.left) - p_width; + + /* begin wet run */ + p = par_start; + format_unwind(&s_stack, s_start); /* unwind format stack */ + + //MoveToEx(hdc, x, y + l_height, NULL); + + p_width = 0; + + while(*p) { + if(*p == L'<') { + int talign = -1; + int n; + + pt.x = x + p_width; + pt.y = y; + pt_rel.x = p_width; + pt_rel.y = 0; + + n = htw_parse_tag(p, &c, &talign, d, &s_stack, &pt, &pt_rel, l_height, FALSE); + + if(n && p_width) { + break; + } + + p_width = pt_rel.x; + + p = c; + if(n && talign >= 0) + align = talign; + } else { + HFONT hfold; + RECT rd,rt; + + c = wcschr(p, L'<'); + if(!c) + c = p + wcslen(p); + + htw_assert_style(hdc, d, format_style(&s_stack)); + hfold = SelectFont(hdc, d->styles[format_style(&s_stack)].font); + SetTextColor(hdc, format_color(&s_stack)); + + GetTextExtentPoint32(hdc, p, (int)(c - p), &s); + rd.left = x + p_width; + rd.top = y; + rd.right = x + p_width + s.cx; + rd.bottom = y + l_height; + + if(IntersectRect(&rt, &rd, &r)) { + DrawText(hdc, p, (int)(c - p), &rt, DT_BOTTOM | DT_LEFT | DT_SINGLELINE | DT_NOPREFIX); + } + + p_width += s.cx; + + SelectFont(hdc, hfold); + p = c; + } + } + + y += l_height; + par_start = p; + } + + EndPaint(hwnd, &ps); + + return 0; +} + +LRESULT CALLBACK khui_htwnd_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ) +{ + switch(uMsg) { + case WM_CREATE: + { + CREATESTRUCT * cs; + khui_htwnd_data * d; + size_t cbsize; + + cs = (CREATESTRUCT *) lParam; + + d = malloc(sizeof(*d)); + ZeroMemory(d, sizeof(*d)); + + if(cs->dwExStyle & WS_EX_TRANSPARENT) { + d->flags |= KHUI_HTWND_TRANSPARENT; + } + if(cs->dwExStyle & WS_EX_CLIENTEDGE) { + d->flags |= KHUI_HTWND_CLIENTEDGE; + } + d->id = (int)(INT_PTR) cs->hMenu; + + d->active_link = -1; + d->bk_color = RGB(255,255,255); + d->hc_hand = LoadCursor(NULL, IDC_HAND); + + if(SUCCEEDED(StringCbLength(cs->lpszName, KHUI_HTWND_MAXCB_TEXT, &cbsize))) { + cbsize += sizeof(wchar_t); + d->text = malloc(cbsize); + StringCbCopy(d->text, cbsize, cs->lpszName); + } + +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, 0, (LONG_PTR) d); +#pragma warning(pop) + + return 0; + } + break; + + case WM_SETTEXT: + { + wchar_t * newtext; + size_t cbsize; + khui_htwnd_data * d; + BOOL rv; + + d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + newtext = (wchar_t *) lParam; + + if(d->text) { + free(d->text); + d->text = NULL; + } + + if(SUCCEEDED(StringCbLength(newtext, KHUI_HTWND_MAXCB_TEXT, &cbsize))) { + cbsize += sizeof(wchar_t); + d->text = malloc(cbsize); + StringCbCopy(d->text, cbsize, newtext); + rv = TRUE; + } else + rv = FALSE; + + clear_styles(d); + + InvalidateRect(hwnd, NULL, TRUE); + + return rv; + } + break; + + case WM_DESTROY: + { + khui_htwnd_data * d; + int i; + + d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + if(d->text) + free(d->text); + d->text = 0; + + if(d->links) { + for(i=0;i<d->max_links;i++) { + if(d->links[i]) + free(d->links[i]); + } + free(d->links); + } + + clear_styles(d); + + free(d); + } + break; + + case WM_ERASEBKGND: + { + khui_htwnd_data * d; + d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + + if(d->flags & KHUI_HTWND_TRANSPARENT) + return TRUE; + + return FALSE; + } + + case WM_PAINT: + htw_paint(hwnd, uMsg, wParam, lParam); + break; + + case WM_SETCURSOR: + { + khui_htwnd_data * d; + + if(hwnd != (HWND)wParam) + break; + + d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + + if(d->active_link >= 0) { + SetCursor(d->hc_hand); + return TRUE; + } + } + break; + + case WM_SETFOCUS: + { + khui_htwnd_data * d; + + d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + + d->flags |= KHUI_HTWND_FOCUS; + + InvalidateRect(hwnd, NULL, TRUE); + } + break; + + case WM_KILLFOCUS: + { + khui_htwnd_data * d; + + d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + + d->flags &= ~KHUI_HTWND_FOCUS; + + InvalidateRect(hwnd, NULL, TRUE); + } + break; + + case WM_LBUTTONDOWN: + { + khui_htwnd_data * d; + + d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + + d->md_link = d->active_link; + + SetCapture(hwnd); + } + break; + + case WM_LBUTTONUP: + { + khui_htwnd_data * d; + + d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + + if(d->md_link == d->active_link && d->md_link >= 0) { + /* clicked */ + SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(d->id, BN_CLICKED), (LPARAM) d->links[d->md_link]); + } + + ReleaseCapture(); + } + break; + + case WM_MOUSEMOVE: + { + khui_htwnd_data * d; + int i; + POINT p; + int nl; + + p.x = GET_X_LPARAM(lParam); + p.y = GET_Y_LPARAM(lParam); + d = (khui_htwnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + + for(i=0; i<d->n_links; i++) { + if(d->links && d->links[i] && PtInRect(&(d->links[i]->r), p)) + break; + } + + if(i == d->n_links) + nl = -1; + else + nl = i; + + if(d->active_link != nl) { + if(d->active_link >= 0) { + if(d->flags & KHUI_HTWND_TRANSPARENT) + { + HWND parent = GetParent(hwnd); + if(parent) { + InvalidateRect(parent, NULL, TRUE); + } + } + /* although we are invalidating the rect before setting active_link, + WM_PAINT will not be issued until wndproc returns */ + InvalidateRect(hwnd, &(d->links[d->active_link]->r), TRUE); + } + d->active_link = nl; + if(d->active_link >= 0) { + /* although we are invalidating the rect before setting active_link, + WM_PAINT will not be issued until wndproc returns */ + if(d->flags & KHUI_HTWND_TRANSPARENT) + { + HWND parent = GetParent(hwnd); + if(parent) { + InvalidateRect(parent, NULL, TRUE); + } + } + InvalidateRect(hwnd, &(d->links[d->active_link]->r), TRUE); + } + } + } + break; + } + + return DefWindowProc(hwnd, uMsg,wParam,lParam); +} + +void khm_register_htwnd_class(void) +{ + WNDCLASSEX wcx; + + wcx.cbSize = sizeof(wcx); + wcx.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW; + wcx.lpfnWndProc = khui_htwnd_proc; + wcx.cbClsExtra = 0; + wcx.cbWndExtra = sizeof(LONG_PTR); + wcx.hInstance = khm_hInstance; + wcx.hIcon = NULL; + wcx.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW); + wcx.hbrBackground = CreateSolidBrush(RGB(255,255,255)); + wcx.lpszMenuName = NULL; + wcx.lpszClassName = KHUI_HTWND_CLASS; + wcx.hIconSm = NULL; + + khui_htwnd_cls = RegisterClassEx(&wcx); +} + +void khm_unregister_htwnd_class(void) +{ + UnregisterClass((LPWSTR) khui_htwnd_cls, khm_hInstance); +} + +HWND khm_create_htwnd(HWND parent, LPWSTR text, int x, int y, int width, int height, DWORD ex_style, DWORD style) +{ + + return CreateWindowEx( + ex_style, + (LPWSTR) khui_htwnd_cls, + text, + style | WS_CHILD, + x,y,width,height, + parent, + (HMENU) KHUI_HTWND_CTLID, + khm_hInstance, + NULL); +} diff --git a/src/windows/identity/ui/htwnd.h b/src/windows/identity/ui/htwnd.h new file mode 100644 index 000000000..9e7478803 --- /dev/null +++ b/src/windows/identity/ui/htwnd.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_HTWND_H +#define __KHIMAIRA_HTWND_H + +#include<khuidefs.h> + +/* +We currently support the following tags: + +<a [id="string"] [param="paramstring"]>link text</a> +<center>foo</center> +<left>foo</left> +<right>foo</right> +*/ + +#define KHUI_HTWND_TRANSPARENT 1 +#define KHUI_HTWND_CLIENTEDGE 2 +#define KHUI_HTWND_FOCUS 2048 + +#define KHUI_HTWND_CLASS L"KhmHtWnd" +#define KHUI_HTWND_CTLID 2040 + +#define KHUI_HTWND_MAXCCH_TEXT 2048 +#define KHUI_HTWND_MAXCB_TEXT (sizeof(wchar_t) * KHUI_HTWND_MAXCCH_TEXT) + +HWND khm_create_htwnd(HWND parent, LPWSTR text, int x, int y, int width, int height, DWORD ex_style, DWORD style); +void khm_unregister_htwnd_class(void); +void khm_register_htwnd_class(void); + +#endif diff --git a/src/windows/identity/ui/images/Thumbs.db b/src/windows/identity/ui/images/Thumbs.db new file mode 100644 index 0000000000000000000000000000000000000000..a80265f86769c7c1326186f19842f6d26393516c GIT binary patch literal 116224 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 literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/app_notify_error.ico b/src/windows/identity/ui/images/app_notify_error.ico new file mode 100644 index 0000000000000000000000000000000000000000..8dcb29e7a66f097ad2f67b181bb352fb2421d3d9 GIT binary patch literal 25214 zcmeHv30#j^+yBYFjGZJTQT8NcoseWF*)w)Rk%UUfPQwf{F_{S=BZO3v3hgEBixz2< zHkGuHZN~NgUia_!i<#$r=6T-F`@H}6{rvyWJ#*aM=RV8#T<1F1b*}4N_fH5V(Ol^0 zw8g!VSl>hlXCZ{Ty6m~Mfe@dN*S4+fxv!EC#mxoE3zTmruC^AUbxSTUe{Lp(Ul%Sf zdtTpIh-Dgz^6KKMrVvqm6y@=Za+Wg{<<-UdMnWhr6QVCLpbN1AiE;h;Lbdv9FaL#q z_R9a*D}UIFjF6BJLHr*7ko*slKaebg-~P>Y#MXA}-((`g&e73Ph<5eex3Q9yvPHg? zQoDAFIz3Q|FI!aD`-l7^vV2t7hkQjtJx&~vTC%mZbv*LNd|Q5jd^<<+xqKS}LRr48 zW82U9d+4n#S3r4NyS++^=9>}c;|1s2ItqDyQ;^^tGVEmK9l0l5N_LJX9ML{{;D78K z_et3eF-FI~%~z7L9UZ0eTuWYlq%8l0l+SNL3AEExUR<7!3ZNYFnX-KGz-$F~<LC&O zDze=Ad`C2Y1Wc$X&gHk*33PAA%_zV)p5S)&$nv+et?$*5$acFOA^(V-<MuDmoB*B^ z^8C)oCt1HI9N*d6fls*n5h-8xg7f!C`HqTw>BX1%TWs435T4$lsS}QG(LA@~XuD;h z5IvC36`0P7d>$_TmMZ)q|A=VF*;0is%R3&}BV|iH*SCXKWx2ARkuQx4*?-P=l<@p1 z-w|~Gd-*3A|DVfqhLkU1m%8Q=OP9p|iiZ;tC1m-{&`bR#8yG)+o)CWG{1Z}J;1AA_ zU+4pU@eAaC@j?zjp5IJf8g0nwAuIp6f+GK&e6iM7P|#1FU*D1<UlJtT_ka14wHRB* z4fVhS-<!&tZz#VjDZ1OkZ*h&2-9!)hy{Y`(P=3eqkgorS?t6Ja7*D=*-z%!5d%pNh zx>t!$()~A?tXKLZC<GZGAmvN<5c$mI%kTBKzId&UG#=@HLeHSv|M0Q{ugaB!q%c^z zDao|UJBT^RWmYRvRg&oeyXyL9WU#mu(t9Ohd5UCb1^EIlV^J)<uja=Nl0=uD-{O&3 z9fDJ^_{Fm%^53#N1^|=?EJsK(+4EkRR6X$)`h&%(Xsg1np=lr|ATDLaA&4b;%g=kT zX7Rj-<%=Dp_#R&1B$K;#R!We5?;sK7&-++ovuKk)JL<@Vsq7h&f!8lult0U)8=jAx zkmQtXb>kVAsFwlq=caa2zFfRuoy#6k%#oV}CFJj!Q>5o+vJ8GENiKZx{EjL4mzPux z|Aa_85y2~&S8p36UbUQfwf>jVUepgk!rD**qJ~%-a=k&se9qIaj`V+%&%r9vCEIgg z|6#WmO!f}^R?E84%GS0@iQe(us)uy915>;^VP$(njN?1@2Fw(=gLq%uo7(b>LgP1; zW)@3A67%4grJb185TXRCHpO5YBK_rmd+|13X|GaM!+a&ClMd1fB;Mct&BZf>t3iVX z!ug=JhzWBNCPum<J}gH3{DZR?q0v(m7Gw#}>#oAdY??4=(m?on`UqX+2BNH>Oe`=k z66ufQgqg_<5gzU>^mK=b1XpX3mf|BeuU;*#oj)xC{QN|A+EdY>y|Os6f1l{9+*xFL zxro=VD}=e8nsBhT7Lj3L!cs$7g!l!C82_`Pzp{qt*Px*o*-J}&sLK@lO=b!IANC51 z88gJSi&w<*#fycH(?Q|s;USV@<3-`~0x?9bo2V+Q5LsDq;`_r#MM6xFs3<EEty`-K z(`i#hn-(oZn6;V6$;uY?+w4S;&lNF3wTm!RZZB4u&JwXfcSKrTswgce7U?OE#g=(9 zMVO_T2y=H9v0+i-ePx}{AFU^>&CSKQ{{6(*{u;vF{bvyt6e0?9pNn&+e-Urr)QJZ- zuZX&e8nI;NEMcUfCc<Jo#RLC9aozoz`0?oXVxn4CVZCXK=nR_A9;z#TJ%3T`vv&~w zKE9&D_dAi25-C<KTP8B%;zX2_xmZ2dL_~!7i@qvq!az$~tY5fLyn3E5@^W*8+b^zS zq?U&8J?|vkj_wgj5y3*cb$c;ROG|j&@D#uO_EG%wy_2|c@uE0vqAil5;zVUZjmS@n z7hP3U#6VS5VL!u2ysE4eE;gpZ!)%raI=D(ihq()fHKxL6fq`(CWgs%|y9(>}Ekwd` zdlBW~BnlGTao;aKq(=!OJtN@&S_JvIi(F?1;d;tRD7SBqc?c4@K_0?ztbvH}a1nKt z#loO<12MWq6A|cfRJfV;7YE1o76}O+qS(Vmc<wh7-&wC0E)F(m-%m`_(-W?i`rx@1 zLaRY*k>X+_W=}K_HDxIx%g<RHuv{x@f?R~rkfEZ1s+#b0IVy6WreR*)M3W|82?LDH z$;KMkl!fyGBaxdPB)&14B6QSxiP24(h%j>#VcMd#h(5JVc)Gg@AJj{Xi4rQUTZ@>( z<{}FiQ~cb-Zc9tCOmCRjZ>lX^9ZZG0^HE_iP+RQZXf6VLJjJ7+D3KE5CoKE-7jY3@ zB0W7&L<ZauM-Lnpbv3ynGcHVYYuQ5R0^=6Ur2*#a;O_k*F(N>8!<^Q{{3`tYJ;gNr zX~IZ%v?$9>7qvAtB0D=u)M4%2a`zO0K7m35eB2zktBT7-SYVLokH399-Nmw5Mq<3G zvUrl7E`C0KT&VHbJYB^PHtWSmtcjSQKoRKfD&m5|L^0-Twvmx&t*k8cR5e6taj~@i zS;qWluC-$a;u_yjWQJ!-*Qh^S;iIrs`SO~xa?zrS$0^@yZmnB#=gyrfeTS~~mxIHQ zC-+R|?EbR+^kIuC-p`)B`oYq=B}-64U*ADi12>evS72i}W!e`NrcYO0Ic19a@NQq} zFW?GQsK6aK@HJ$}y~{S$n{E&MT;ZE<z8SlC$`tFL)UHb984)h2!jK_Dnk=&!WBhg2 z!&8bHDF4maIa9)hj$XQ8!Ga}smT(8sh0=9Rv`}6rV^aV4kMas*zZr|mwDVAB%{yFt z2}XblQsE{|w3<AzG1l32sQ)C3HL@<a^w<?E(wa4OB>)16P#&d+40(d$LUW&u@x-QX z{j=K6@6?{VkQHB%ts2@<vzA9tCokWm$-TOzOOM;wOl+lhiObLL#KX=;<11EVXRlDs zk8&Beq)xiH`6hM0i9LO6ez7sJv6&n1*MqO+Fe<rxwyJ8HUC7y^XHZ(7YYZ8(TWEf} z|GT1$wM`8Ss<-sd%36nOI?)_%Ts1AtbbCm%RX=QyS9s!E_9*g3YSMfAu<AzkVajb~ z<sTAR4qJiJX=#mHR#tkt_U6Xx?m3k8?6$h&gdA)8wQH*}n{6!W%P*h9<%bQMvoc|B zUUF_-6I>h0&UGC8e!^SpDfVj(P=2r;2n0A5Xn*+t)9kciro)!6OlX&P<sKK`U3PeN z%53#1QySUZ4@UXfhI)enE#&0~EMAW0P1CejCak|$tF^g_)`<HNtHa(-nGzO;;wYb3 zVXQm(6&JUFMjo)Z#1`cnuT1!{*SK7*&01bTPOH`5H$q{Q9t`5lo-$GI=eAsaWIWO0 zlI_cfnVK%|+_~Kw&A28*n!GgXc<?=t;4(1a@{0^6>b;)7&SL)QkB^DAZ*N&*Yihc< zv-+Sotr;_hbh+!eaQzf6Jy=%WP~F&A?_J=!bss17D@6I0EmzL5wO#yp{gGSdGq!E> z^*=xRJ<1PeG@N!B8ykO$9Xar{+ppKVbNQ8}i*0SSA0OQF48^xCJutLFD$k{X$KGU< zv2jzk`SaU;=)O^x2*nq5Zd{|S-XOX)ifb)BV6t(H{S+Q457*uh>&19i*8GoFdv!LJ z6RqsLNMn&@jot8mw*sZ|X5Gtm=c%A&{D=QlY{rbynHxC&(DP3^dT772bWufxUCpds zeNT^+%6Bj+FP}Hhm`Q_cFxnrZV-+}Zi{8YEpHLpsWcF<B#<OCUjU4%O=FH8sN-96K zl>x}Z6>L;gHhGU6X@&BwfJXzx)mt{66};?3;I^4FU$jV{hvw(an>tTc!QP}=Wz&|C zBVSK6ZpG!f_>`4%PPRK1-CoOFi!PXK)J17r^9-dPbgET$-5eP>ccSscR;{>v;*=?! zEiLogI5yYRB=^E%hR@}v>Na9PhJ#hCC(RF>t7D7`L=qn^vb5|RHZ<Vcj^OUUYy^2^ z<)=!Bs#h9Xx1B%oYm_&hNTi+!w$)g^T*F|kO7}h{JGpgTpyv+CxdWAwiZ(Ytw*DH$ zxjb|E;MvQUuQW6?+&FQg9+y|NFO{#}u2F5}@$s=S=FJ%8!HNb`1`KFvSglfC4lEKH z8K80=u*&ulgPd2d){Ys23PjQx8eB4<#88FHGZ;nlQ*~wK7uy>uDfv`$1tM@F8d<q~ zd$r1#a$PR`x%@n-e97WPgL87w{g^SEhztx3tE<NtZt~frqhrFC%@<d7wU3XFcFE$| zZ4B3<e=a{~j^*+Jix(?zZ`pYfPhHsH!BPvGHtBr&6sC^HiUHf1UD1Kc7zo7WmdjBA z%dGRFMYGk_@d}ix9)t2apLDo7k_4I&p*(7!yf)SiIzajQ4p9Enrm!h%n-&&Uv!FmT zDpGkGfb!Z>`9A~t^vOrBu&~hN6Uw84ygW-4bWjh7M<4_ANe{kXls74?W-=Pe+Q+|t z56~xhc@rCB?xMat%M~5~Mo>QhrqCx{ZXX@k*c4*PeJ;<E;;#VltZe#2d1~4DKbQhp z2l9Eu`u*Q|{o8LPZ3g8wAq+;@^~HTJ=ioVCy8n0it^V3hb=hwza@jtar6FnxqJ%bV zEEgvikpP?3)MSBZ58K1T!yPtUjIcLfB?<}(V2>z^8tkd{u<yMM`!&eLT3DG|itO|( z@w&JYdv{fFe80W87Z@mNa(%=!-Tq>znyT1gV<X<|{h5j7SL!6y~tY^kBE?X=n*2 z?6u3v3Pf5=ib#zK6S-Llu&0zo3)q>txmm*9)m_Y<HA~!u{hOMSBHq{3iAiX4pM!&# z3d|M7Wug*x*U|k)MFjTd;jn?t&CFmI>5KO0qaer`HjJ`lA15TlV0_rW7Z(U$AK2e& z8saFh4TMdxY=MdR@Zp2lykG(B)dj-Z+EN4s1&J}}%fsD8WTmHwVHz63#l=N<dw7cD zuz|C(vP4~3oe0Nx{V+alZEfM~>;yZuKv+*R64;K3hfz_mqhK@dUj<vgKxl)8y1*0d zd0bdpT8h_YmEzm|hlPW^y}0D+1{)|w95!1ft~nhQ<z;0eEg?l{1B1J}JKAX?T%BD- zLR5?}Gc^@<u%UO`*b5soQ<0t!CI-M(n`dGoGE>sUZ#5spsiRKf5p3?ApvMCrKiKK2 zq7`W3>g+5m_4^A0eSHz*gmL<L2)*_VguT8V?5-fOOkY>{95oY8ur0^J)(mnvEZkPj z!kmVQlqf%OTwepUDn_05LK(K78T<-rO<Icyu-#nu?}x4A0sAscq#s^}ay3FtSq*cU zF04&WM0%8`u)+M#g8jB1_WByoJIKXee1KnI7W!JCrzP25qcv19Z)!pVyk>8@K<J~q z`*AbiOAyY79mHrYEn#DABSyoXR)ei>z_fRA0xuK`54S7ANJ|xT3KNGxS8dEQ#Ita7 za~1C@Ys68^ZC|vz1N669wMxJk5y#-`xaH|7jvhWNF1fjhGVoyxa3!TAh(J$2ao;ac zOjA=6haL7yHgRG?f^c(j6?s{?qByr$WG1AGo9-T<PXgw{T-<<9#m?MJ6vGdao1QBk zz}6g!`Ux=!!fe?paq2j*mK9^Z!5^SuUT&_4VjCFqRSbL55A!t^<uhQ9vYq;&?t>^t zTh^A=;!Rl%)=Gj%Nr)2Xon3@J)`7lOf8g{H4&di3tdmbzCo$l4Ys`K722I3h@a}PI zbCx)NG%EjPN~B+XOGEsvK8!nZ>^B`9o#`t=L$8O1hT3e*Et;w*u&P6?W_5XtNJ`Kd z+ZF{vZEX5(G-$P5VfXoTc-6})^;}OUU2Xs```UEP)hHc*<HCNaiEcjKx+UhAAKr6O zM<>Z)qs@wqU1w=5FD;!j`Qu0~u(sP)H@C!T4XCZI$8@$wbAO*j0d3lxp1kX0m@U!6 zt!{~l!@H{}37s;2pKH^)`IS~hMH_ZaeEaqu(L%SxHEW(dX?Ny_leyEUPq+ET(tVV6 zdESy4mv&`ZhY<x0cU!Y&Z@b+;x?DA!{@T#;_R5yp7A00{eI|!zS$|E`qIS*0NVE8P zrb{giZeF)+sI9FvZk^`N^1JKRt-~^jo}@op)5{p|_Z2#ywftUNd#Ba9>T12BVV|~G z=U*a<GERwXvU}c=F8vG|_nV@f+!Sv$Gj^7mhp0zH^dOor!PtIt{0OyUKiw%*PL3_f zsMf5`&|LJi_gBrfH8Lf7F(|$^r9$mHdgE<*z@*qpdSAJyRZCmjM{Np-RN`~|Yb%mH z_c&|p`PFn$HQr}rbZXUVezOh3%!l%OJ>%LO{l&h{8r0sXsxqxwQ`4%`qE;IwoiHDY z_j(n@yKAR?yWqa+eOhVMDR_0q42z6KsYPA`w@dFw=<ST(ta9FwZ0OLLmsOVsSEW^3 zXqKC}4t!qvw32AaPOFNPI+Jrp#pqv99kx$pMw;e`@>2t~N*6sPQfr#0XSUm-Rhu!# zO*^d)zC3tvr|Pt{39+3A*0fr*h$zphy6FJpcO9!tO{-Q12M1r?(WzQ9t)1Gzf&E*h z;=Q(}U0PgR$7NMjbAvCxzr16I^gca4`R9(Qsi{QMRQeAqEgkm7Wv-@{Z0x^8+oEGf zyeGfeX3;I8CJdT3Vu@O;c6tAzRx_8Vsafr`vasvekvS}S(-ZmYWbJa3qE@BjXTB>i zeQL!8_&w34C+!BQOk1Lc_guf!d{`S(qc+-k)poM?ga%aP?Q;X>!?=J^tGsIYd(5)b zzq|u(00kbjk+-iX^<TM%{I587`0IU&>tRa~203O5e|kYdI%K$;Fo2I+4Z4T-A~x`~ z9zA*#KDjVahP?pmR2ViwTeUrOv9st8eFuXddLv!hC-^}|V!JKcL*}x+cY@B)gI;90 z?BnSMJ(VllH%Pj=J=#%)&$UkMXT8RH7y7~ja$Z+gSA2kuHG_P0f}SmduC|5`J`j5W zS65e20KH%j-ORGQ1u#TmA7Hc0K+@wH@Rjr4AO&)o<+&F21Fp~|YS4eYr>KFBVtwW8 z<Sglr!_Y~U*e5hWJ2B7?hmEwM<J_S`RfPuV@By;k!Q50FKYkqTu|5wJaI0a@k|q40 zyX-MuW%y%1efT5{u$M@H?(%?sD!@KP8Ex`@P8+(0b?{;AMNCI)3wz5|*qb>?dmsml zgY~X9aI((R2F^NQbOF9H=%gB8`}g&43jC`nVDS&GUgO&?w=h+o+Ej1!TrR3PN^7NB zyZLK+_B7OOi|1CFCWE@^s;a74G+8~MdmoGTGj)~@G3snFw&jS9BUENs^wL?{rAHf! zWh2I!5AN8kg++^{{TolWn9^*-HyyhV>Da=e%Zf1z&2)z~)0j4~L-!%QEVRcgRI;#W zI6}YghOQmD_p%s1c}|1oqS1(TeJ4y>&|$Vk>rS(JtEi0oX5PH78Z=nY-eOSel|u(~ zY}Raq%2z7~4OO<Vn62Dv=*aFpIyM_IV&uw!47u0Jt{r-O1vDxHTJW<)gP~};#}E}2 z6$^`h?YX7;Uro<fc-b3?k2(J*(*X3}4GGbN^_DaK{Oc<%CXVc_ZBmymHZ5MdcyaO4 zMGK3UE}UDu!gO}AvEk6-MhH*r(z1DRmsVdDcm1k)aku8pi@PK7y$g~uQU|1V&6^ds z;na*%bNOBVjQ6cM4|hds+oEZK@>i{?P4gB{f$)Tql9D6tYmtT{wM9at<UhX7o^qx@ zpF0$E*PlWH0x2vokRtu=P;9s-MS1Ka#8=XwHXUemkM5+erb-jLb|IrKDm1Z67y4RN zne@6U(~!=cXh7$V)K9r1X(@Li%}&asB~N`itB{roQs>Up7j^n~?JA|g-Bd}ZTMruC zO`YDodq=-GJHG<sc>^)WB1}c%xj>}nUtd<{8|lQiM=97pkRt8{)1%-JdU!vWB7!0) zH8q?<E#?r2L1VjhBZF?;X<~O(nuKK76=UhrnZ|U%7*sma5d1v|IQuE9kS0<e<<1z3 z3iVY{rGBcwjno%q_^#c(3k~kkm9%^GCZnMv>HYinl$o4Lk0QfAjvYNB4v3wQmLoAw zwLnsMVDRT3FUt)UWM#354(;7f_ilskq2Ux49!ZbFLMSEn0cCjoOghSfMghxY^&T`y ztt-aRl_r6HhN>zw4rvV1aNrt@c^SZTM(W37Q338Qsx$!k{gDP@%$#)4#*iM}NVlgJ zO&vCZuot1H=^6Aa=P5n#aibNB7ncCAJCIr<jX~;&1XljrYm227ZMN7<)|Ojnm)%af z@#_^zh>W6mq{N6|dh#Tg*6a14-VMJZBj7hg-;;pfi07fJ3K<}cW&FUaqr!6l8uK{t zJOF?5cn0Em5YwHpcL#2?q1!`^hWG4EGluC(_;WL}s30$&!f(5i#av_Bwb7yuXV?#b z_%PBOBt-p60Th3ji)~ihXbZ4!-Mp2yZ`n>eZSCpoPp2s^ERy0MMbgu!@f2=lM(w~u zhU#h<LwA~twP3_^08ED6x{yBbj|6Y);;s#vGrc9exND=#AmlUdA>F&vP_^zf9QB8( zYtYD^nlycg4t@OikzmVH(X;226&p@l=FXyxGiT5N%Z<NJnPm6`xX&T++SWz-!`iml zvW>P`ZY3LFx7%t<JGR-;E}I?n<G0__gS&o|nVmv;_pcEck-i4~jCz8HF!vLI+lYCn zTUQ!~H8~nshhq#wnUAo>2V-7_NNbd7-j#*}yIzkTz}<s}_UuV|y|rkJhB^)H(nG@k zEaw^U7gAwHBJEjYN*2>+kma0NWNx~M5DW3LQ^$_Hws#>JA$3JUBsL<lthbXbu<ro& zof39?+GDqi_U>??V+Rh>?Q7@h*|R6Kcbqz*xHJb7)v=Z#izWd3gszZFz&{GO5eq`Y zF-FF%E5|+ryfU=A8VyrZr{Nks3GpyAs&{V^N=<1{LqTV@Eur6j`;DIGJ*Sd_Vk*hc zqGOovjnih(7US8pdiG2*orU>WWA*|2ZeH6zAo1GPM*3>!_8l_pxbL>zDZ_5>K>O_X z(t(}(=;%HN^0|G9o~C+JFW~R4)QG0^1a{C=A3QM*bR7%)%s(SlRUuD-7ubiXF`pwr z&Wu#=Ny9XHkxp+78q-sax(Frej<!B#L<0MJ34h76B6{(>l*&ts2=N!Re(F@(YGO?5 z=FFyVaNx6K_H5d?Y85S6xUdKd;3lwJA@x4!uusD8z%*yvJNMB3U3=-^o&%6chiLz9 z2Xa6A6Z!ewpcLN=G`@|Xah=-H1n|%p@bVbU!5GNaQMhv&fix6roAD3DdeH^1k5*SF zJ<P*bojOue;NCsOkbZytlz#vHcluCSEZslWy{C_FKhp1*ix7`r=?JzUPTRK95$i3q z+iDBhbJ}7>du(jz%K7um3n!3t;L8HP+y4>!?tOH4-vK(fe?NVD=n!PmMY?s#jr^}2 zqlDvjG`dAo8Uxx72VaboaI4a2jA1nX9?iTCtiv!iUaM-5pA8!}rhW|@l2!W_bamZK zdKT|aIk^#(kq}Oqi7`}|n?sM|lPNtumEbd@bH@(QrWw=7cK%%2WHN_V%$Y;WOiXCS z+_|)Jz6sfE*ieuE>`%YYe$afM{T@09x*u{lK#qG4(zp8$lH>kEbmG`|bo=^s^0{)E zZr(UYrMJH$hXFmIm#`ky)M$iS4;qcNFsi!>=nq~3=1~~iu&(OVs8I`Q)2I<0Fdax) z*B$ApuQR2k`BPGiFC{#Pq|~TrN_`YfIcaH-*U^yMF%<VGhOV7IMP{?6k<C2DZ-S%Q zIkX&OSb_WM1#`)6qh&q*i!QG8-Jx%>UUwmm64>`0lG1kvj?hu0(@v-8j;AO2UB60R zw|=Fn2WRN;Xie(Z32PD9M|9&cfbV-CVLZA$G-v?E(YSGA+Nj=<vTmM%9*?5*6mN<P z@}h*Wdz2FK0O=8>Ka8dHN3jzA*szDtn-3{6B!aRY$B_A=d9-DYG4Ri&HS#&&ajaP| zkL)*ZuE+nYs~erc9P9(_kL;7t{9EAu{_wZ-{h_1e`tup`_x7fMTOQ<p>k3sxyV0h8 z14y-t8tH+*NAcQ(OdO`s1M)=;dxmZj_6v�Jk?KB_&Y&gD^@AzC#JY_b4$ugpwja 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 Nl|lr5Zx?I${{ZwxG8zB? literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/app_notify_info.ico b/src/windows/identity/ui/images/app_notify_info.ico new file mode 100644 index 0000000000000000000000000000000000000000..8dcb29e7a66f097ad2f67b181bb352fb2421d3d9 GIT binary patch literal 25214 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�Jk?KB_&Y&gD^@AzC#JY_b4$ugpwja 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 Nl|lr5Zx?I${{ZwxG8zB? literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/app_notify_none.ico b/src/windows/identity/ui/images/app_notify_none.ico new file mode 100644 index 0000000000000000000000000000000000000000..8dcb29e7a66f097ad2f67b181bb352fb2421d3d9 GIT binary patch literal 25214 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�Jk?KB_&Y&gD^@AzC#JY_b4$ugpwja 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 Nl|lr5Zx?I${{ZwxG8zB? literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/app_notify_warn.ico b/src/windows/identity/ui/images/app_notify_warn.ico new file mode 100644 index 0000000000000000000000000000000000000000..8dcb29e7a66f097ad2f67b181bb352fb2421d3d9 GIT binary patch literal 25214 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�Jk?KB_&Y&gD^@AzC#JY_b4$ugpwja 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 Nl|lr5Zx?I${{ZwxG8zB? literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/bitmap1.bmp b/src/windows/identity/ui/images/bitmap1.bmp new file mode 100644 index 0000000000000000000000000000000000000000..1f07309b90df775746ae8ca2e8e3aaae3dd48729 GIT binary patch literal 1270 zcmZ?r{l>xo24z4}1BeZvn2`Y@(EybPDH4EU5SxLa0gM<L8bB-{20|d?z<~om8YJ=` dj2Qm^2eE({2th(1{wNp?fzc2c4S@j)0RT8AlOX^A literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/cfg_applied.ico b/src/windows/identity/ui/images/cfg_applied.ico new file mode 100644 index 0000000000000000000000000000000000000000..4fcdf843c8c4a41c4dd55f3e0ce0707f99a6b63c GIT binary patch literal 1406 zcmc(eEpJp&5Qg6-CE1XQ)~u?4Dprpmwd<SIRIi)x4{8vriX|Z_Ij#ao5Sa1@Rw3vl zxUHe8qDiw!Ti2w4;W@KggD2d(b3WcZXWr+|xmQ}_-rQ7OubxOlMlXmiJtNjMpsm5O ze;ltpPNxz-O{dF_mZ4^|nPxv{y8Zr1>+9<p3<lcR*wEJ2mWIQjwzs#nv$LboXrzOK z1MThYX*?clcXwCQ*Qx#e)H}_Ui_o|EjpkQZdi(hcMn*W%h*(Yy&Cm?Zx|Yi&j_&A= z?&yx*w>9*J-k3D>hEAtI5A;9}gg{T`$vmT+jn?ot5*eFJsj{ar7z_r3!C)vP7z_r3 z!C){L3KIr{!C){L42D97!C){L3<iV25)BT6!#YQ1RRD#)-;D?Yy+1N6j=5vxBz0J- z4E*T~92SSA$^wVO;cz${4o8&=4u`>Ea2R^&91e%UVF<jEwn~W4@@8J;?bU$_@5ZVs zHQo)ThH1l~!PM|>{Mks-@NRh5ag;YY{tf?ze?Ovzf1@@Hh6Y2U4*l4uN<BRaJ#9Zm zY7qDZYyn5$S4Z19s6oIHor4Ff<7QL960igifnmJ@uml@}MS&2o@FU2>nFK7+>rpKI zZRn?f21CM-FeD5KL&8w66AS@E!jLco45T4g4@1I`FeD75CeUF>7!rmAAn^~9B^#4P zP7Y`8aAf7~Yh2IuQIp9;hlhtcIy%zv@v+X%&UAWus`K-6U0ht~^72yG*VnqcyVL#s zy>1^;A5KpAj;B<&e*?WEU(I^nK20rF*VEOtu(}riZT=U}Bgen$bwfdZ5O0Y0#7p8U T@vw?t^hEafy^5+`ub=b>y;{N6 literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/cfg_default.ico b/src/windows/identity/ui/images/cfg_default.ico new file mode 100644 index 0000000000000000000000000000000000000000..d6e7f24e0d3228aa1738113f11352a2ac04cc08f GIT binary patch literal 1406 zcmc(euTNW16vw|LENK=5m@34ETJ=#_JPD>6ce4KgGf{<JvZTp%h;7Nl40@t!VjK-- zC<F;<GU`Ys&OZ0N)-2d%uf4y%z3+TK?|WaREk3)us_X3wY0T^mtxK<HTN=^VWEeN| zt<7>K@zrcL4D?L3SS+;oxzOW}&)VMJ)@U@+&d!eZ_VzR$kF~$QuY-dFO(qjfr&Apt zAM5DoNQZ}qn!QV%oTNVJTDc5;zy77``+L3r@)aYaIjzxRSQ?t48Jcx1hb@lo=#K8_ zj^1x;=ncKGXy^@{L4h9VfgT8fo~)B~MmZb3;cpZ&ZZf6Hp3Yz}7z_r3p^#uO7z_r3 z!C)v%7z_r3!C){L3LOT6!C){L3<gUyI1CQ!9GR5>3S<8?TF}t@Bg5jDJ4Q}XhoxfR zM`z-&I4l(l91e%W;cz${6%`x~gTvr3^ynN8hrwY8ypp~m#Ao=hu6TPoP~qL!RZ-*J zU}~5)3>r)g@5YafA`S0`cO6H0qvPN3Z}|5kYWO#@X)rVx8aec1Bb9o36h``fjARh_ z1#AIF;8#c6ImjU3h|a--)p2uEz!I<o5P@O60<Z)(2AcvQVBt%n4rdauM9-sG`rFV? z0S$(PAz?@u5{87KUMCm=hJ+zu2pA|sV?PWDL&A_SP@0AgL&A_SBmjwjP%ODI+2qu4 z)(%Hj?Y_tLTAy@!daARtGo7EGYd)Xr>gr0DmzTP^xzX+It?usb^ziVY)oP`urzbuB zPJO(%;D0<L-Toiw1NCav>-J@8xxQYmujRM8mj7-27te;{UybWP*7*!kJ*N$MRfOy8 GhWQ`3QQgV_ literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/cfg_deleted.ico b/src/windows/identity/ui/images/cfg_deleted.ico new file mode 100644 index 0000000000000000000000000000000000000000..2ef11c7d11ada0ecc655db6d6a886301a516cf1f GIT binary patch literal 1406 zcmbW0u}fS*5XL{jz#$ge94Rakt5{DZq_P)mtnG&U2R11#&DDW`u$@H`NRh_B;2@A9 zPOVL>ECe}-c7_y_-|XFsMXKbzo86u7?e6z`v+tx8?u`xg>(vu!%;*JC(lcU3BicS# z^uu^%NGB3MO(u(umZ4^|nP%T-y8HG~tE;OTjYe8uU)Sd5rpDv3wzjsky}hlSogEz> z9%_GoUweCd+TGpN<aO%cAoWfc%6aJP#SdNF-01D6&lnluL?dF68k(UQnw6G|DUR;w zj_&A=UdtMKLvJJvy`j@7&;vct10m3pJjpY<v(Xy<#zw{>Q>xq37z_r3!C)|SBp3_^ zgTY`h7&;~l27|$1Fc=IS9R`EJU@#aA21_(J3=S)f%w7Q$`nnqt1bV$PERMNj<ZSA& z^fK_!88|EsOD_u?4u`|xa5x;jRB$*94uiu`rE@qO28SW=O4?pRd=@u(FK<-`D!d!B zda3blFf~jY1`Vc$cjIAWlZJQ0yB|mQM#sP5-|(*^YWO#5(_m;YH0n^tMpgRTqtMgV zF;auTFJKEe0>6H=#X$`MjwlXZtRFXv0+xU!fCvoxCjd*ZFqjkw0SiBZeK?bVC8{2s zrM`wb1vD5EhJ+zuNEi}^{yD)AFeD5KL%_f`1oL4?7!rnrfvpL27!rnrApuDIgUynK z$s}hFCwDlq?_G0T&-Fn^M@Kq7KGt+P)yc_;&d<+vc6O%A%S&BdUFrJzTDP~iy1&2I z!^4B_ex=@@p7KAQP~G|uRL6cjt9>g?&6n5O@|yqb*Zf~|sa8nCdTc8Flh>GQZFt@v vGUcy);91TeIBIR}@t80ChWvl~WkHtp!QdtFlz2mYSq9@oOP($2_SpXh;Q->| literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/cfg_mod.ico b/src/windows/identity/ui/images/cfg_mod.ico new file mode 100644 index 0000000000000000000000000000000000000000..65e3691ffe37eef19d71f7f78ead1365cd01450e GIT binary patch literal 1406 zcmc(fF;7%c5QPs3CL2Nl%{FYIR8mhPEj<YhHQmI2Kx47S1WQd!NNz`=CKfjQ0k^V{ zsiBdIf`n|MbR-tzcjp0OW$D|Ud*7VBch2|b?yj`JYh^`sJ%1#PxqC`<=`pdO5o6s9 z^WFWzT{@L`YdRe!Ms78m%{2Qy)6Z`owYa#b(P*UQ<z=m|u4+6UYciQ=eSKXU8yni) z-PQK?wzjsmw7I#d>5J6PPU@XbmGjWo(;qs$ywuxIpD{ARiAKbb8k(UQnsqIQC64as zj_&A=-nTXMhTcdTdP8SYpa*)O2ST7Hd6H+8voRX}Mj>O9DOL6i27|$1Fc=Jl1cSj~ zFc=I5Lt(;TFc=I5gTYYfFc=I5gTY`hSfasUa9HQatPY?s_g5o=K<|$Xi(~E>IYk|o zY6BlSHx7%#Qf-05;cz${4u_*!1&71nFgOgobq<Ha;4lPU$yhDKXLylUdwX}F!n?7m zT8(#usbShMXfQRr8y_}`G`t($bsXi5j(@|y;opy_;os;@gQ3CD=tDm?x>8S%!pzu@ zksbto0b9Tk_|?&N4tfx9MCaha>bThyummgtL||B70a${K!J<G2Sa=iE;Y<RS==~^` z{%z=|fCfXtkT4_+2}8nAUndv>hJ+zu2pA|supWklAz?@uC{3WlkT4_+2|(f>6iYTH zi<}xx?r>z)?rU66^g(-jd)nXM*TKPo4i67?a&n^M<71tjo$37iTo)G?y1Kg3_4T!G zZf^ARSL*%I5x?Up-R-}DK2fi-Ubm;I`R%ndS*mOPtgiWg%kO#kcNvg|ak93y_E73M m%Kehc|D;Rge-(JRw{N;#sxLI;U-6n=O>g5R^DE*F!M@)yXTp#G literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/chpw-dis-sm.bmp b/src/windows/identity/ui/images/chpw-dis-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..230ad36a406bd2606a792c457e72942867da5bf9 GIT binary patch literal 822 zcmbVH!3}^Q5W~2DPe*V92Jr9vu46+<Lx7ZvBwVh&wg>l<_f$?sy>O3uw{#Xh)j0v{ zyE;LcV*csj8WR|0DlOV*lp4Vj*Xv29X_JjiBW)Mso2edT=js?iHl|KTAjxwJp%@Qg RF#?8|I*u3^{BI{)!VjzPl#&1d literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/chpw-dis.bmp b/src/windows/identity/ui/images/chpw-dis.bmp new file mode 100644 index 0000000000000000000000000000000000000000..43468cb2a9a68415137887abb325a479e485320c GIT binary patch literal 2430 zcmeH_O$q`r429#;8wldw6L<huZoHD$oa-3pbsi;6B2K9&Oraq^uOE-hT<?#)%;%!< ztoNk%sCOp^jUu-)pNkTvF-OK=r;Urn{?Z=sC*>b}jo-0cr6-pC866=>VubGHMy`9g zpZSf{=yNTJ%=eo-&=I(AH2K(_c5&MeVmhWY4xByUK@!`Mfokb+U)|;K!O|#tQ&Ps{ li(K^r%Ta6vw8_NxOCoh`2lQG{=OMQit;jbnmiBjhcmi)E#asXY literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/chpw-sm.bmp b/src/windows/identity/ui/images/chpw-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..309a9178dc2a6bbd3e4df5280f49b852c54e39cf GIT binary patch literal 822 zcmb7Cu?@mN3^ZZ^O1iWt1Eh^aPnnug7=@lC7=v-1^IzlyzfHjMTlV>UabADE411|I zAf9jz^V!L<;xhMHFut}-qz7}0we)|KwE^i=AGRQ+NGT}gS=8_9T3#PAP9!sO1mu`M z-UR+>s*~{(eSRg2CZ7=;zTVum!t&qy4@IV~cQ~$njTnImyUbl|TkQ=v<a>!V0z!Qi SXl47@8ZE)5!XG5aDdcYyKhCQF literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/chpw.bmp b/src/windows/identity/ui/images/chpw.bmp new file mode 100644 index 0000000000000000000000000000000000000000..688da40df318687db35d1ba6c97c2d68ea4a626d GIT binary patch literal 2430 zcmeH`I}XAy3_#7u4UiD(ih2OnN=z(_ow*4dg`G=qj2<UAaTU3?Lr9<!Ln9~hFR!kX z9M9J^gmJ)ik8_7}i*p4VTmepFJQ(9L%rjsf?67dDvH#QufHr&DHTBXBeUGl<Hf<Jd zbHO<jtO%aY%iW7NFAwPYPo7mFx4Qw#beIh(<bGMNSweKia-&BpcD*`}dx*<DTCd~! z`=D9elL_<GB0b6N8~LbU>d^`O!v>c&5QTKH>JTTAZg4T6Ph5C}`BQFq&6=33H`Ryu g`#M3>?LJ^0ZRQ(<N0>Yl^(0s1m6{8eIyPG$USi1t2><{9 literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/disabled.ico b/src/windows/identity/ui/images/disabled.ico new file mode 100644 index 0000000000000000000000000000000000000000..6b6a9078c1dd7d32ae13c81919d5c85046256023 GIT binary patch literal 2166 zcmbW2ze{9S6vxl31{EY~BPoP&EtXi{N(fA_7q+psyW~HxS#V%MvegI+!i5mFNCGL+ z_!o>UWFTHhr%i+nQ*4kxX1W)4@Z@vOy>B!jMaX?~f1K|<KR)li_f(`KPh@+0U9A1~ zMC2LsdNJjTk3>FR5cw2soBlbyBa4hQeA{O3HH1~)f70*Uiq6GiI2<yYqW%+AThX~p zvf;nAlpgyfZ*yg(*k@HRQ-q-kb%9(Q+Kp-#ceYkafF&!D#UY)wkt?2n&3Q&kh+Q3g z--qg8d8*ik7Q1Gc@7D}wTRt0^=Jl|x#ADkWs$Fi&Zo^mgHgG7JSXjbZf<@KayRr>D z=I-u^RiiD`teVA4+TX3B{jZUCptM;}b-19~@96!)@MP9)db94V*R0)lCri3EeW`=# zCH<Wa#*>rLxap0?v+ihozc!lc(qO7D>6e3vzFTWfCKgG2)3aQb({o#me>8i~*Xi5z zjx00!qRj2O)0%yIx#_&$Hrd6(-t*xvLz(>aoh&Xc%JTBEtgf!g#>R$hZf?rf)|Tw- z?8xr!t{fg7%E7^b?C<Z(-rk<{U#L8PuJT&0MSsuo<MkhM{r<h2eg6X$0zK003Fcx3 zw15`Sf@yq1<`U5(i-;c4Bf5np^n~uu7(JmgNk-4;8K;bp(G^><HGAd27)TyS6bK4@ zw_cthzz|>vFa#JJ35EbefFZyTU~o(r0t^9$kkDapbQl5*0fqoWfFURv9087C9Q=)d z$|)jageAffVewmrB~m<6D3Ub7;>W;dWD#MBu=ugS5#fk%L^vWGepGNo7$OW220OY4 zM}#55ko_-KE=Ne6a$)=N+UY<=?S!8nHMJ8=iK2-D38qBt#HM{miQ0+UOmwS_PW?pv zM16ZiiTcTO7!nMLbFjz8srcQaFf-a?<P0+PGHe-+Og(?J#=#k6II?kYWB#}a%CKZu zG7y=9{tCd73CuWUgbWKmI(c{|8J2A4;Vkwx*i%4*L19oB6b6MsVer=ph75zkpfF?@ zNJGaT28BUkP#8!}M~6XSP#6>drG6%v5~w&uazyM1hk9=FiSkswm7}90IX*s?)6-Kq zKR=hNt1G#<xR9Hh8@au`mGO8icXxL(old1`8ku}h`R2_VzT-ZpYu|uP<a5jNnrYU* zwP)7m8*V))b=3;(%>P@DF}udvY(^`THF+NdmR6?<556wc!9i^8zSY%2=CPKeHOp$g z+Xfe3w;m661hcNlWJ%=Lw<2$+BA0)PeA$S6^+M$9&qM~Fi2VFYWOP}4%-3Qp_AiFw BZvy}T literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/enabled.ico b/src/windows/identity/ui/images/enabled.ico new file mode 100644 index 0000000000000000000000000000000000000000..22821a8b78a4e279660ef206e3b920dc4946d893 GIT binary patch literal 2166 zcmbW1KWj5b5Ql#{JFyEBy>46?q_~LV!quX}m4kc3+qibO<QupN=+d|j1Pt4~MG{Dn z#$O;JMT&T9yNSWz!odV0cY+8{p4ro%dqqe>PMY1F-_6cEn%k4qQKf$Wjr{tpl0IR6 z)42Nl1L>nR=~J{m_8HdEIwKdc^UK3$TUgcQtGf0T#)rjdG-7te%2#H-!uYu4!~ePV z_V)VieJg&2zt_6I>ig|>f)&3i{xZR;-`rj%Sn*4^4>JN^_FFzKd&^KFnp>{UHk4JP z&I0<h9+k<jsw|ei%0~Qof~tvH__JM$1jTO=Ec|(b#fIiiGghsNMfo?s;asMu`pflV z#TjU>(a@|uZcIH+M$cZ=EjHDLxlOcIY^;vCJyf#shZ;^cG@N#IJnh!Uuf4iwy;0A8 z9Cghe+&0X{Zfj-}w~pBqe@c<pp`UX<$9=4$O~$fl=J~oacWXb~80Xt3|FH0LKKgZ} z@h{(LeSKY<o15C&+S2y+wsv-Qw7a|O-PXaufzHm(bb5NKlamu29UZCu!u0m7>1*A} ze$V>(_7B}YJ?X3Of1pCZB9^e+xB)Go1+?HA?~sQ?^vEKjNA!s9+Y)+0PdEuZp))C? zXY`Do5i+{r8@}aIIWPv22NDG~1>W0Io*}>xU<fb-7zz>$0fqoWfFZz8FkuKV1Q-Gg z0fvGOLx3T`5MT%}1Ve)(z!98-lK@qrh>Q`I2up;e{A5@n#Uq6xNh2(!4cv?@A}kS> z(iS)(91)HPM}(ua3XTXvgdxJ<t&4C(7$OY0{A1;&h16+2_@%wx9jK_Ch%2q8c7iEU zG*KYIl&GD!nW&wpov2+NN2!fY{Y3pleSbuW`ib5o7!nMLKKNs!E9KXtFf;mNqz9RL z8MX{Zre1lp&Or|{9N9VeVdZhNDZ`Rs$v|WZmLmX5W@9EOBV<_kV&vhOWLUEIqp<jC z@TY(VgTY`h7z_r3!BCD93>gN4!C=TRkOm_j27|$1Fc?UUp~GM>7z_r0Q9qN+*k}Yr zazy+Hhn3tO6ZNsa)%p3kE-o%~d3mX;t1I2y-01rHT6cGMy1&2I<Kv^ApP%*e@}kLP zqVb=mZ{EG*J+A4lzXLvzuXvu<UHAUOwZFgLj@mywJZuT>wuHuA3IlGs&9|@~=sjUT z^0rWF{k`C!^UEXJz6OK$+<DwT&P^Yr39T3prH5nw_j*`Y`e{RYTBA>2Vk7JUd%=jE F+h16w9dZBw literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/flag-critical.bmp b/src/windows/identity/ui/images/flag-critical.bmp new file mode 100644 index 0000000000000000000000000000000000000000..0b4c920e61781e20f29e05e309c7078237d71b35 GIT binary patch literal 1014 zcmZ?r{l?4y24+A~1BeBHm>-B485Dpd1RUXGU;tqlxPJZm|AEHymoEFC)6hr~p0#ix z+}+F8uKO=Kapr&CYmeZV=fds(>h&%E6+8OiVla8p-l_i=uUP}v5AwfzaoK;~hwuLL zJ^cj6q6=31KY8-x|0xR>{pUV(7B0?v>)C%_pjz}W+jro=f4T072z7T~{nuz}|F4K7 z1~OZ<svc}Ex_%Hprl$Ts@Ab!EGkBkT{BP(N@LxBt2yQ0--V^`JdwMbKhM9Ht?AiYs zHBDgkJm+u!Z<{gme^5(1T)$~{E>68LGm5*q|MMQd^q*(*-v3*6?D(JB-U-$(HF?(m zRU0=F)PMEr)&GX275}AXF8sf9_wN67{S*FkA3XCvxU`(0UYHvu%$xsTc=nS2($nYv zm+PJKUpYJf|Cuvqh}I7>$F`^x>~4YW2maSjm_(9Zkp8usH~*KIx!}KL7U3|1xf`AK WNlpL1Y1=lE?S`2H3QHIp7YzV*+x0B~ literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/flag-warning.bmp b/src/windows/identity/ui/images/flag-warning.bmp new file mode 100644 index 0000000000000000000000000000000000000000..f5be298a83810ae8585244cbaaa50d2507c48daf GIT binary patch literal 1014 zcmb7?F=~WB6ow7ylN=#sPLKnn&jBoLJb+Qjf{JLgGTT_%S}tfIwplQ+2tt+xwF!HG zu$b%?wtwasTtZ;O_{0BXX83*|Z|3g4KNkkwvU<aO#eB(pCS0@nqt^>TAdhO>7N@+~ zY)+n?VrZW@MN#lLo8duK@jRbT$_au1Z|gO_4u`?CE{cLXL(?>T?)OLjWw}HYMcjOn zBzW>XeC&33-)<qxGU~eK=4l3bo<n7QxUP$`EGMzVo2Chm^`PrI7K_DX-f<j|`o4!{ zS(Esxst|_Zz*8{{V-g?7F;=V9k*66nO~ajG+xF1+7?7qZvMl4~so&q~x(>eYbMy57 fzoPs2@6C^A^!7zj9PW=8nmt*PBrbl;;r)IC))X{$ literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/flag_expired.bmp b/src/windows/identity/ui/images/flag_expired.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2815fb448637366a99c8fdc38605d18802169178 GIT binary patch literal 1014 zcmd6mJ4ixN7{~9`h+4Y2)gZKGM1&AMmXK1&L5QGQqAjRykEQ4Bf!dIPP(u*~L1Rm_ zRkW04OAaDh5+(E)qA-F$MN$8K=Y&FNw)VmAaL(oa|KH<q+ALAEz@w2?9Z^n5geX+e z`W@~vL11Je7!2Y+ae8_NS+?geHRV7c5GYuy%jJSjCqoe4i1#0;Q~{VwW50S_T)0tF zGltSqH)?B{2h*oGqWFU9Y8h(vIgG~fydHIRGD=FWFg)x;G#W)mhXR%AiN5*J-Mxrd zEQa1*D|xul*{S68q18GdNyo6;cfcZ<Orod93PsuE=SU<%xo04X&uDI5%ByGfO43o* zg9~X1hr?)TnWbJnct3^o%%h^hhEOP!k)yv~p}Y^-c@)wY6$LRm>db1{CXq<MY-agi z|H%J|_Vx*+(`lqqDfIQtV}JjId&T2%y1yCHhx7KY*N>pM_#8IdHlJNr*Bs6I9+j25 zSYO}cchcHAP5N6{EVDWD^LRYa>t$#(2WV&*AnhZsmCZw=IYfQ^Af0buFpR_R_vh%@ z8}fR+Xlfdw|1ODZ;*MZGFT`y@ZRQR7d_HV#?P6_h8_UZZSXfxa<m3wB$eZ^IQJu9A literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/help-sm.bmp b/src/windows/identity/ui/images/help-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..49e560fb467274e42779c54e477eb7c6b20ba541 GIT binary patch literal 1014 zcmZ?r{l?4y24+A~1BeBHm>-B485Dpd1RUXGU;tqlxPJZme_CL#+`9idIo<!$8hgny z-yyg8zi8>A|IszwWa&S3>eT;3hYtNeckUcn`ir`z{r9b%{D11A6=dlTsGIVizvuY> zlAdW~=?|%$0M>6=*!_RTV$$8PWA~o_u?-XcOIIxUFI2zje{f~@|C6UqlkApFJ9hs! zFX{uk#kHvY|CK9ONH*i(;UoX0ist<1?>P9sWy)-l^@Gx<Ug1Qr8}d7Ghv(+aTmH9B zn(=@7yoLYw?%n(U%$YO)ckSN&KeeIvzf|ka|3Y;e|Ib^#8mIlM*R1)!{>Zuin{K@N z-?-+$|MaP={)f)o@!x&<mH&qIOMr1X1E*e)8$n^SX6?HF(`L{8-#2@~|E6j4|JO~P T`@dnrjQ<A@AI7Vf+&l;X<v$x^ literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/help.bmp b/src/windows/identity/ui/images/help.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d7d4eafacca37cc90c33f900c2a82289bd54c295 GIT binary patch literal 2430 zcmZ?rt>a_>12Z700mQOEEDgkr3=%++fx!bPe}oT$!2&?yI&HwHxsY%j4TnJx4qmx+ zIyv2GjlIODb)w?UA-7qybWwCoH<isjb?VfiLx;|tJ4a=6i@K)y)=r+fXa$wc4XB&K z-*ddAXBw5w4XK>~G}p4Qd&Xj7olI1`?by91wqb&F#S)?VO~IAjCr_Ow=y0OUg_yEw z$8PhYKA^*0i`uVTxq{zl63hh}bnx&IsiHaj9S2*c%%-NfK(q7;Cjy<E-$@`2@ws>N z<}Iz0W=x;AaPQu|XU?42wR?AJL$6fpPNBMu^OmnB-@U8XtXY5L+@>3^8rK|1pSmh^ z<_`DeR}AZyl=RHNZzxa}pL>Ckvu5qOX|w0{&0f$nZGPS4xeXI$96WrOprHiJB}P9k UHTc|%OAj&Pqvk?FiDuyd05`6Lng9R* literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/icon1.ico b/src/windows/identity/ui/images/icon1.ico new file mode 100644 index 0000000000000000000000000000000000000000..c2746b065c2bcfa0ed621cb018262323a49744a5 GIT binary patch literal 766 zcmeH_u@QhU3<DL80jQ{5W(-F2Xc>ns%qtu-P=I9FIyq@_ks6HHWSon%Gdl`&pr(}$ k*gf#+`-YR8rA#{nF=Gs-lbWR}I|LoM{rL0?@CRPS1!#7awg3PC literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/id-delete-dis-sm.bmp b/src/windows/identity/ui/images/id-delete-dis-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..68850ab8797e9a8e37eccc9fec3d8f92917612f6 GIT binary patch literal 1014 zcmb_ayJ`YK6g8wz@(C&P1?dDyqlk-@q88#tQP;PKTKHHWprEEmFoI~It%V?1Sc#$_ zDq4sj{z%%Bof|f!SYw1-I5Tq@&bepqohR<&T1D|Ftq&6JB-~24Qr%1ISIlozDh4?Y zhXeit@pv3wuK<t7^9_f?LFaHdjQM<y&1Q457SBJ!<#K^>EEWqi8V&S%J=AJ7bUGaZ zfdFB<-70ECA`#?rIi%BRM59q?wOY!}W;2XNBMOBAg25nGtCb=@n-$S+x6x=cP_0&x zNF+!jpU)H5=ksB?Tq<%||0<OVMxzl1g8{nTE<{m;)oLX!^X2#Z(eL+9@?T%yu-omZ z#&9?U^UXYS97la%daOUQ*-TM`^`%~a$MJZ?WHLdqSR}5=WTIRU1nSLivtF;)sMTKJ zbUI<P*^o>ou~;mq-hRJVtou`sdE<E=p->2RyB!9D0n_R9EKk-bm&@pMI%qbV<lpUf z(;Vdcb9<>&0(--FJSLq~Dn)<h98bPy^I?3JvvVvKL$BBSvwy~GwOaHSGMS7tkNQPi I*~caOClXBQf&c&j literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/id-delete-dis.bmp b/src/windows/identity/ui/images/id-delete-dis.bmp new file mode 100644 index 0000000000000000000000000000000000000000..11aedd577570da0ec2024610d86ea48b80249fd0 GIT binary patch literal 3186 zcmd^>S4&(`5Xa4X$QKB~zy=W?#D}%uT6R}m%UaeF6?H9WbXQ%wqHFIF3$cM6v0_2R z0%Ad9Ls7wk^$Ykh{<$;Z$|?k0*N}%?m|G5<^E)$V=08^%l=t3>FDKz{E7&K&J_`2E z>a%eFeeHd)vLX|&o0}W_k7h7rAvrn4EHfHQIab%#*RP71RO$qOe}DM-`9Y;p!OP1V za=HA`9335DV`BqPPtV6OnamC6=jR9v3`AL3nb}-7H+N{YTDI8OSVTodAuKEmK|w)~ z$z*VLc1BuS8f<NCh5dNJ+uK_hcY|83MqXYX&#SDgG>a>jD`7AgkeQi@^z?M3rluk( zDG3n~5wNqfLv?jEYHDhr*X!9|ih)G(g?Thi=I-u}&CSghb5$x|l$V!tj7myMP*_-q z?CfmlbUOBv{1(@yJy}~@BR@a?aV#=25^Zg5FXmCrsMUdJX=!0=YHC7VT^;YCsHlkj z_weultt)a(pA?IZjt;YU#MEd)F)%QI{{DXS^z`r=&CSisAiuM+vf%3KikO&~CtQt2 zgZB1zvw6h)`ZW@>v$L3)nZe}bB!-8FnN9H`pL23@U~g}a;^N{bOo~5oFD@?3=4!R^ z*x1;>+S(dcR#q@KH;1vYG4%HKqQ1T!QmOO_k8(+jFYPBdIM{5iPN&EI{yvV5j<CDC z%k262d5$&J3B^}rP>o6?5`=_=JdWk&=JGey>q|9tb#+y!gLK^9-s0@+jQKP+Jv|Ly zUtg{fVk#601Ox=Y$;la|rKMcQG;TB+;p5|j<KyEOGih(%zc*oMXbAWB_c%R0#n#pq z78VxxY?VqSTwGk3p-?E%*Vo5uH#Ro1f20$KhlghI#CQ<*+qYEw{JDdjogFMKEn#$Y z6rG)&NK8x=>fQrCemu?7uC6W|92~rgCvx}p_5>}^3;O26x$5BHAjH>C(5DQXoSay+ zw#az?q#Pm*AWaPq59jlv+NAf_)Wq}kpMO)mkfxIcPz_K`BqSuT-qSec?DIL6erIH4 zpsK2hHIVY7qN0LzJS8QC^`7c<YHI3DxuKz<tOcY8gM)*dJERE(1qDJsPv+hg9Uc9q zOzIyL-}Uu%&e`$taddZgqqeq|`waC~svAq=X*!?I?(*`IbA|GsbZ}&31g)*DtbJl% zH=SeYdqYD5>-@vR1Fo*Fu)V#_T0}KSXG=P?yuAFTe$t-lA7pWH5qEcYti9B4D85uH qq<5D3^{4sNx8vjE5f>N7Eb3Pi6BB=pXSxs4fY<&@ny&M&-hTnh2Waa6 literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/id-delete-sm.bmp b/src/windows/identity/ui/images/id-delete-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3447d57a1e559b16e2319b71205f08cf86f98425 GIT binary patch literal 1014 zcmb`FT}YE*6vtod(nzTg46JUVl>`zw5`v(+2*W-GUgS+hS`q}U56pr&nMJy$bJG`e zO|;c0lu^)IO>Og?WHPr&+bp6K2`dVND2Oh8J?|S4$QBDa@IO2Up5Om@&f)B?{I*=u z!zi*rs8FatC{NlT@~===N|L7Ekxr-iA3E98hN<y72QU87zB4wxuDQ_6<YW|8eVX;H z1DD#V@Wv_k#MmN7DLCX|+3FkQ*WO}loeiVWOIcYjkw|8bqb=Rk1YS@*G{athk~I}B zB<VYOg^$Qz+bYgbS~|e+a46#+%nlEh@Ei@Jv(ziEFx?d%Mz`R8=&ZruBNz;4WVQKO zT^?>e|3FXd9aqNZIp&YDYLi!IHIL%rQDk{AEAQx%iOhZC@#{tUrr)!1M~BX?Snp%q zX0tedrKsp0fq;_HBauj8Z+9~F_A^5>A34<%revFoU3FGMV`EfRIfXh&rBWGrt%rC# zj@{|TRDYJrS~I7c+nAVmrr%E{lYeD3KCDLAfBq_aeAATo#@K$s!o%Q5*7ug~oyE~b zOJttQp;?aHf630i1jnt8ocShuCnIkb85I3%KH4rEZoHN=|3FcAqK*??_<}sOK((yU e@4u5Xuk|zFyN#`*n{%yh+@Ah_VwcV%m-;8)z3}1y literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/id-delete.bmp b/src/windows/identity/ui/images/id-delete.bmp new file mode 100644 index 0000000000000000000000000000000000000000..5b1b680300e248f255dbb16f991fd7367d2e6a73 GIT binary patch literal 3186 zcmd^=YfM{Z7{?Dd7Ga|hmlzNi(M^Lg7?6e$gO?e$5i+17vXyHu(2fomV;ckFK*`J` zFeNZ3mr`Vam5C_W!9<6n$faCbC@}5}xnE?{T|TH^{=VmoHsf0x>lb~K|2ZemIeC8P zdCqzM?|Xq~cT&vqCPx>d-9%1A7L+qNK3N?silU`oV`F3ZKPoyd5rGjxlO#+?{ztuX z<M6S&pcgL^y^!C7=kwICztRo6P1#;-gq3>~SlR7{*uG#s!{l(z;>3ybCiA^Q1n?{$ zfJeC&?qyoMUZTOCqCVyw>@yl+=@1Kw8ppO*1qQBbFb_dN$*{80z}osY!oyQd;`)Tf z;E-Y%eqRjZV9gMGt90<L7=)X&2iqNk2=7lK5X}>A4B@I^=X99x1~%h>om~bLidv(& zZ*yZ2_U$;hM4|O#;M9%6|H~1)Q#}M{ejc8&Ei~k&s3lnL3IS!IHN<Rf>v8JTC8K%t z%=kr!kk~SVgzu*zxj7BtjY)(zj5BPy{#OJAgj-->JgJ$k8(^{O5Fc+Ek7fo$$06(I zIegSUhl_2$Af@##!xtzg@jj{huwn!+3-V#Pqi74)#-<SxNv28l>3xC2I|*t3G79^a zk=L_?PdXQnLD*-0m|<!Txjuqjo+8rE(Jf4hT7j1r5B>f9CUd!b5xyGz6}5NPQ8l!N z63q&(s27o`nnU8v2{^IEpe)Cq;_05cUdR6Z(dg~%HHjOal#B-L41T=(8*WV9W7uT_ zE6C}Z$DRXXSXfRN7~6JM!Nw~JHapS`F^bxNt7|0GYPHea&dyFm#)zR^d4!II2WXzY z55>qj3Q50x!!N<gx`oNVomC0vsL$YXBow*1QaCtdZF2vDv$GJlZnYZCq`#}IT#n4k zIT%`d4AtE4_-1?qGE!@H*8<p~S$Nj|4BSQeaO0<;u&kWPU0PZLN5>BdPlD#=7L$1T zf2mw9M>tQ2x~?I7H+c^ggR98xUO?(kv+&Q7<3J!6#WMMp+!YlHD3#4yV)}f>p0!#n z&ZS(y!6N~9!;=jc4>o)c1>kf_I_P`G<}u|tl}ZJlqsOr~wGw*^y5N}Iis!_i;;mpV zG#ZWBnQd)tV3YnGt<fN$c7*X>wtRr`dse0r`}~5T)9K90O(OjhRJY?a@xhbLQ;2Mw zK;ZQ|c=MVLuNC*hUUC)EQkhwq0mma5FQEP4<BoY;Xq|<qX$rx_^L;7@@lt^r{t>Ze zWd=nEP)GcT_N32stHcKvk$!s?LgKshKI1Oa!sl3&S(#z{81yVXMEBxDw9MQ`_3#>U z$gCt0-=)3YyQ~Ksp4hBRX>kdP<kv9&;1BdIKSIOA2IKo_WIiLv-sZtgfU>E{tW0{Q z&Ye#|vAhz4tB;{1zCiCcX|2<EH|_(ZXI(ZsPd~?{G8uTHIE0B4a7-WpCn^SYU)TR@ KJU#cnaQ+)%qlK>k literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/id-dis-sm.bmp b/src/windows/identity/ui/images/id-dis-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..ff0c38e4d169e66a2356c1b41a0687d9bb51dc1f GIT binary patch literal 1014 zcmb`Fy=nqM6oogWPx1&UeS$nduyi*f2E;;kv!Vt?Wkm)5A_yz~Y+*2hxP^k1#e!gA zB`#(W5iD%P7fE|EGa(R2F&oH*GfXkxy>srp_u|K;z~YXa9-<qfYoZI`mYiSlekBN0 z-*Gyf@E^+O^GGC~5s$}zDU-=?YbeSSqR}X1Sw<)ng3srJ*Xv~xMUl&Avsu*ZbyO-9 zXqv`y1p)!aOOnLpYqb}crisB|fKI1_QmKSkEC#>d4@r99^6hpT%jFXD`5cqU1cqUt zP$(c0i69t!<nq1VD~`t_cDo%Giv{~jqtPHeO(2;}a{0sIfPViC=kpos^%~RZ6s=YZ z;cys^<8XPp1N;3R-L3&uRT)<-7O~xKU9ptkY&K971?hB}`KB|pZQC{9Y&J0-k1-q$ zLA}%d(;m9rPhHoUht+Du@|4SEq*5u@yk%L8r}gMPm&;)`o4MvGHyVvVccEIXl6mz1 K#JctQ^W7)*o%nbF literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/id-dis.bmp b/src/windows/identity/ui/images/id-dis.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3c04aa2fa9ce7d3d71eb6a12dcf43521dc09f2e7 GIT binary patch literal 3186 zcmd^=SxZ|{6ou`3=^rSCKK4m`2q_qiNlarj2_z0t(8M8W%wVF418P*9H8^0MaK>32 zP;mfJL5)!n6hv|S1OANFyHB|yEfmVV?MpB0+>nQSXYIZAIx0ipZ4`T?+~+5*K5+G( zt2a>}xz8U@<Xu!07607d-{XHQuh$2g&HhJKT^$tED~Uvk<m6;%wOVL28YCvFp-?D( zTQXUkAZA=#JS-LqtL*G-WM*a}H8m9mgMpnvtybgbijS8IVk#6Wc)eZ}6&1nlcEjOt zAU{7J#7|30LrO}@FHAb85FctZNvN%@MNLf&s;a6`US5vk;$k?RPUPj~G2Nv3wN{}_ zolcL=&Q4aLPzcS<&Fl;n6%|as&1OSZRu=Sn{R>wp(`YndY-|jpqoWua8bWVxFFHCp z(Ae0>bo+ch6ciM|Y&O62Stv6j!;F=c6)Z0=V_{(dlarH--QC^IG*?zuvRUWm=E7t$ zF|9&9WU*MWzrT;&-Cb;NZ)0_J6|=Lm7#<!5aT^*MP*zsPyhS}GPbDR31$oHnbm8RW z1n1}HI6gjR?6tKu%*@PSaBvVb<6tnzv|6oJrqk~a2x9K+?7-!6<Kf`}S65ezPrp&G z$H&K+*82K-`2Bu1SE=+f&d$yRF=@}*+e4U`n84H16D}_=ad2>e_4Rd3O-(URH8nM{ z{wkF!jEsy3=JC5t+^=69I64X=91df1bCdPAzrP=)rKKR>^z?iajmL8%kqDOsE^cOD z5FH(jn3x#m7q{Dki;D}<dBi0T$>nn96F&E1V`EtcUSD5}&ZK>j$z-4mC@Cr7eB}b| zB;`H*o{*3rI@9Cvfc!~0*w)sDy1F_90s+|VcBH4LLn4u2adA;pE@cd50Tt~?Utb?u zT3VQ2DRXr?9m`#Q_F}y0-lYES?d>to&dtqXU|;~WCv?xy-KJ8hMAb>NyScdmdFAx< zl;z;`^fbD<x<HxB-|J%N<Nu?zwUuSh^Yb%qZ*OsUc!;H?CD4A-Y?VqSwzjrJ)k*Ba z!a{6pY~b<nk?jrLH`HI+8_Hb%d}6;T(<#eya&j2UXf$Gee*UlVUZ0=ldUtpCPgt+H HVtxM(Di)S+ literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/id-new-dis-sm.bmp b/src/windows/identity/ui/images/id-new-dis-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d2a151c373c1a8ca7de594afd7d0d5548a26598f GIT binary patch literal 1014 zcmb`FJ8QyF7=|s~+x~=({RMT^;Am1&3r=E+w;*0nbP%<IcsD|WQBV<e5Oh!k5f>-x zqzJnBPrAO%IVDpqap(u{NlwV~p7UK8_Ul$h<5^js6g?=qS9GI$RMwyPywmB3)Ht8d z_z%V7arpfLd_LbVg~MUZGkE?DZnqmQmkSPu17@=sCX<PZWm!!=5{V$2%_5abAsUTR zFRRr`d5+^W`Ap^=-EJ4nW)szF70F~0UauDxiv=9_qRCe(6|7b(%;$3q1_Owqh(sbm z&$ruOHTjPZ38&Ku)9Dn8#e&`?m&?ImFdz^JQ2uy4V!2#i^g0|4Xf$Mu$72M8LA2X# zlu9M&^?Gc#TTCVsT9^HPe~~9Mu-olWtBG(ros=W}sMqTt{VSKte{$;lY&IM4JdaQ) z1e?u<*=z<9`FviJB_73M5&eE2k|e=sG(r#rv|26dIUEi(Jqm>aMxzlt9uH($ruAnS zhH_-Dl>@1Luh*mhllNFGM(0S)Z@pgA`>6X%&rYWUG7IT+TIomQ>fA4RUGwc5-XZ3z literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/id-new-dis.bmp b/src/windows/identity/ui/images/id-new-dis.bmp new file mode 100644 index 0000000000000000000000000000000000000000..61c2fd21abf89d775c0bdca75429c043ff708aa5 GIT binary patch literal 3186 zcmd^BSubN@7&iBlxpE<yOyWw!g&4$$DpgfWks`KGTP-b$3Sy5fb|UsYwjlNpf`~AP zNF)+skKgi4-*<AFL5<}kb2BG-d)n`u?|Z)IeV_OFDi6v3Y{i$G>C@g6-%Rn<6rZeY zO`pH7!!K4=6vB0VeU1My42B$NwW;rl($X?fxpH-NLttPa0s;b{R4U=?tAI=<`)f!f zo}w^4J*CiSG#qhpafptNMtFEQLPA1#4+@0>PeUs85``&~$zd=Uke!{4%*;%rr>7%1 zIT?f>5fOo)prB`%v`;ZQR4V;ZQBi^N@^X}vl%TM%5V^Uz(CKtYN=jnh#Q9IIVlh>! zV6?Wjax^tHp{}lu_fS++#Qe2dEn;F~5F8wA#ubaHR)=ADco;)NL+J1CM`vdzT3T9A zQ&Ypdb8>Q!l9Ga`s3>!u#bQQAMqy!L0rT_on3<Wu*w`3jx3{-5=i=gGuC-dNhFmUZ zu3|N$(IjAJX9wHc+gM*;$I{XgrlzJaFfahZt*)*{K|uj)*u%qvx%&GDh|-Wwr^nIJ z5l&7{aBy(I*vreyn4FwMZ*MQC#${z?jOFF!#XOD1{AZZw=jYGHUefmF<|g#|Ox)bu zpt-r3>$bSK$hjUF8DXwfRaM-tgz4t?1INe5=Jn6c&XAg#%C&z9!_s(TV-rS4M{##| zhrYf(I5;?fbUQvi&YG&Ntwm^PD4d+WV{mZLj5jec0cU4tuD_*qUmGLbw6t{W@Biew z`uX|sdU<(yFdB_;baaG^iwonet*s$8HWofUJ~%u)H1FXx3`@^xKGj#D@M8>ndwW=0 zTeJ64uTUS8FWK7KvUgZo<Go{~AwegC?@^v<FT!<ye~*NO1X0|CeNn$iBodGZP(Jl~ zJrWZWLH)71x+*|oYio=9N0=w(o|Tma(kJDwp`ihlm6h!Cc6N3k4Q*^}nDIzAySuxh zxrT*>u@{gZP>)dlY;0`U3sfo<YngO3H#f&#NZ+Za#PFnZlk&T=vch_#^P4ymM>=OH z@5E2QARnQ#hVmhdz4uPFBfoWbcSl!O7xxSGUt3!n$aAGqDZIVCAFY3Nb;Z6&y?c6k z`smqP@ayaASsM=z54gO%#NOT>=druHo4HUfiLa%#W@l$PHy0Nd7N1+1BaCHaWUxkW zZ*SRq>AdOb>EZtiRBzIj@b1$)?epo_TYr<MlYZjk;~9(2u<7Y(baZsEAHQ{-kGx0c S1l3EJOZECl&4l^?`u+=Yr(8Aw literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/id-new-sm.bmp b/src/windows/identity/ui/images/id-new-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..9fb17b98c31a33b7339b15bcc5f97f5c02d1090d GIT binary patch literal 1014 zcmb`FUr1A77>5_#XZPI*5kwK8QDI0I-9&_thGC$Th!G7ETi7UfF^yJowzj!{2V|+0 zWT7E}o`@KzrwIS-oS~hjD?Dtm3L@9A+o!X|M94Z2z3{x>_gy@{mk-`!H9xjmign27 zyA8G*lo@QXlpFn5O>eVU3Y1FIH2w#dy4=)v-sSX--)XYnG@ViB@KM#Ta4;HYk2uE; z-y6z=*F`GY+@^kuD?-y)l7^@6I61s%WU1^7&J_L1cCV>_)f>e*sdGKHf>la!W>lkk zK&3n|!vUM$)b9>PdARU}zW66@ze;oY`Fm=gyk%GWGSwYEQ{NjFk@c_izg%S?@fqio zPV=)R_BLl|bO=R1k<gH&<xR6xRmJ1$<=$A1^R_(QFFtZ{JjK4F3RAMo$jArm_AIlr z$xZ#j4k(I(r#Hl@)=ny`7C6_MLJ-!l*>bqunLm1^{h6GWsl9fG6GI91yB2vox<;V{ zA!q6qGMwHpE;)@|(m7m{<Ls4H{K0Qnt=}0O)J-$GJOTP9ax}K(ag1lEA5L=Qc)aLE zB04=i>Az-7ND_gt%3L;&IG?3KlxVpyzwX3hONipi`n&Y+jpu`*N8Iv9=nz7LqW4W_ JZgl_U{u4v&2L}KE literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/id-new.bmp b/src/windows/identity/ui/images/id-new.bmp new file mode 100644 index 0000000000000000000000000000000000000000..54830c1830327fe089be0aa95f594f863fe5af35 GIT binary patch literal 3186 zcmd^=e^8Tk9LMSZuDZPABIVNL5{8)k5Lag)Wr3Uw5-?!E*k}qDKLn(tfFp}U^3oJ1 z!ccQ&;2?)aR!9NDz{bXp0b{t0O@7G)I$(ln_3ryT-gcf^$9nj$@7?G5-aWT{U(fsV z<NbX-B=boJdij#4D+y;3%Sk-pu#!CQua>7A9H_7^jYfn2W5g$=B7`fjD+DPSjB@1> zmH_7?zhVW6=fAuL$3yLS=3pD1{+C!An!t$ZwVe-7aVOS`RajT1f?KH)tB)z@Z&>!l z<Js<^JVs1*WFp=?r^c4A)d)D-gH30;;d`=+=DUa#a3b0t*36XY7xgZpFAN}xL}ZNy zVbXpCebWnnNe?!h?4)&5d4G`0;xN;>g)2nrk9ws1phNOy9R%MEB1Seqvnl=TQ(a)4 zP-6K5T&B$6_#}LM)r5j(6LK4k$ZjyueEy|DL=fF?o$JL$!gVd}z;lQ1=b6Q@hr)Qf zAnF)HiDC?gu8m@U>oD>Nd-qRzT61`{25*pByBGGsIY&+R^je0wjY<&WeE%&}_L*^} zX9A_laU5(PL4K17>4eL>s0HVIA6S_>tco|niD>nUh-cKd@1<uzrqZL{@Ea}-PSNbL z&T)L+HjEE{G14`D=UWZdWf-s~W)v$vse>Rj^ATou_u@UZwY3$oi94YhpTmCf960f} zA#S^c5|ZzIS8vccj;qzeD`N<(@CmpEMnW!^+n6fV0C+qzva-f(?-t|d<>CtDe^!j1 z2@8(Z%)-rc9?1u$Q6wLxdrCn1$$Pg6YuH^VDHGeymPm~7^qhrAWU{?m4A1%-aYY4U zcmhb}J*ZSo!9Uajk5?_=9v(rss2=VcYT@lYNuOO;r$cP)H2nN#(A=#3PdqDErBWd) z`+Woi27~44gH<o(<Jn~vYz{CZGIAbAkKRR4&}}$7FCZ<=jQ;+`*U-v%cyDTI0(;vI zxO{LLE=S~WDr$hEe+#Z$`2*HvF#Lt+=xIjUtlY-NM)-t8VcS_Hf-8Gz@AawZ#7h1U zs-*X<NHjG3iM+g<OTtXg&H-Q2j5OkdyS~4USkkNhMLM{?ynua0zvIHiyF~9j6cpSc zeY1f2`s<9j!8>AUFQEM3)0P_u*<*&|3-j=eGlP{gh!;1`faNg+snh_Wa1tIKbEv8^ zFyeB!d`O8Ooob)ND}fgHN6aF#&IoEwsC&khI|6s#%Qi+^n-*KQP9rzhzPYs4lG>va z5>Z||2QT&l4oGGoQBR<lcoF4)l-Iin^zeO6W@B3ao0m5V4rdAqh5a)lI$nyboH}Sm z??5p&2bp$~_Wiv-4}nYWw$+=yLP~1iBTR}dlj#u{I0d!Z{@Kaela1pQr_puOLVGWD z-|TJBVRO<)*dA4l!otzVGAS;TSv^r+4xTUx(K}P{Ha{6*afy&t*ARa*qPTeEADPx% UFXgUO4xppM{+cc2H`sI5-<tTzOaK4? literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/id-refresh-dis.bmp b/src/windows/identity/ui/images/id-refresh-dis.bmp new file mode 100644 index 0000000000000000000000000000000000000000..7d79343691bf8d24897c7d2c6ed7001bfb8728fa GIT binary patch literal 3186 zcmd^B*Gt@K6m{R1#TQ9nSrkhwh;<Z=eVnzf#uf|eNMegUHZ(RATQm{GhBdLoSRxWk z43;3tssV|?nyA5d|BSP9ZkRz_%_s@`vOoCdH(!~1zI)HP_l`2G=vM=ITs5Con(>Eb z{H__l7?@~2zddWe85nSApM!$~{2xbNUOrM&)6N~?;o<Q0_5IggMMdQg#Y&-YMR0I1 zf`Wn&5D)-=f0f`6%f!S4Mn*<(a&pqGaoxki6KiX0n3|fxhYugFjPL2`h2-R98HtIB zh>MFuR8$ng!oqO<`gP>w<jC98(-VGvelRsPmAQk11Kix)U}t9sYin!d<>g^>bMsR8 zzP?I1pPQSD?Cfl0WMtsZojc+;ZB|rNATu*l{JnPVn%srwxR#cdCTDqP-si@R8<?A$ z(~BDr5QysPYE)HK;lYCkC@n2TK|z7UlXtJLuSacdt=y%gqy)vq#li*eqf)7G_wHRC zenLV5EG#VW`t|GY;)aI)g~yK{%V=q7K|@1>+`-)39PRDxXlrXjYilc-o14Y9p`jrx zEiLi(?OXKq^}*56QMlm!=)YR62IIcBw|60KL_{P82L~}QFo53PUUYPHh@XLhf#~n= zM^8@=Iy*ac__nsT8V>Ds_Gp`&=I7^waiAv5%*-$|Gow3mwjSKR9V`1UUcA7>!~}+i zhb4B6jg1%^8<Y9a(2x$FF(I!@OG`SN)US(+3%tF(<+m#<D_B@q(CweaOioryoM>Zy zejc;4v#__f$IF*5!FxP<^hmg5j*u&3V`ErZS>gHf=VEhbXGhMFTX%PNi7T~D?VrWc z&hOpJ!ut9;Ha0e}y1FW{q#w*lV%yl*AR;0{a*15g7nSNy+`4rOuCA^!SUb|w(?x6K zpYx|rpI(T`7-VH-<M8kh+uPgX=aVN-M4#j-K0aRbSy)&o8dWG<#20FuzMhUhHAViZ ztNs1`3o+^M!-p*(&eyMB@$utF@rC)vTEjYX^X5&-Lq9*I@XEN*?z3mlL<{6oi%;!G zM@N4bPa9+6rl)7%{rkT~@6_t<?k={rw(#c78>v5Pb&BvnO?r8GAvQKv_FlbuCH!-p zx}^@acJ;mK7x`5x{iW7UPEO+J=m=lFe8HzrpCoU|E$z}r@=r}OR~Hu-1z(|1;PdCt zm&T-xZyHgFMp(;gYH9@c`1n|dPyczAI&yY)mKw%6xux&=eADjpt{xs9lH2$1-`D6W zOSDS;vnH|cl$V#ItgKA>kB^TJ7zf56BqT(1N)3#Rj9eO%{eyLtInO#r|B8x=q>jhL z#7N%M)zwK%$+7mXd^7IsJJi)x;u;wlDfY+{IiiMGi&)Q6Q&SNd8Y+2n8te3T#)+{x zy{7Lw`zC+4yu1wRaD03m-QC^NSJ-E$8>LctC7b#%Q&Li-7f>I}N!G#9(NXCu<Wbw# z_1U?~ep6GE)XkHV6XE3DyLXtLo<>(!mvGCTtgkO#1y6gHx?`VbPEz~KamJT*rLywJ m{H&U>?Vv6d$#B?%U5fVPL6cm8Ltkyqy4&$Rhp+WH5N!v&ZC literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/id-refresh-sm-dis.bmp b/src/windows/identity/ui/images/id-refresh-sm-dis.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c9d9addf15b1640dd3654fcfea5fe4342a43c086 GIT binary patch literal 1014 zcmcIixr^Fi5H+OF=1)kOb_G+|X>G7jR1m!ISW#3E6>m{c(M_>p1o6PaO7H>&u@J=7 zLczlGKS}%Yn+eLgn~)~?V7_bSy_q+R{OgbRB3@sF{j*>n1-ldMo#>OW|60!<L?W`w zI-k$@7ZwNv@bKV-!{K0)$z(8@Onmoxy?@PNHd|n|S{b9$=^&TOAr^}vl}aIzNTAhf z*_TSCa6BI0@cDc`kS`XC!C)}J@AvatqtU=<G+zH~HXG-8tvQiMqF%3~QmH_%*Q4ET zquFdC9*=V!i^T$VyB(oWi1~`e;!O>OLIIP>1jFGF9*+m(@tFBY`~AHbtJMno{T?cn zifhT`ayR&=rxJF%9R`B|`;|&1)M_>7QYaJ%27}B?`bwn|5{bkOe!JbGT>g&J>BRNV z=W{rnPLTd?w~I(5g6VXM(P)HpI*r+Ec7vyWY&IJ_K7Ql8{eGYQ`FtMHXjI@XGj_RL zGS}<=(VKc?Hk-Ll(n<YlG#aQ@t4Jo32#3SWk;!D<<R(1zq}6J1JjD>+<#J)YUSH!a z77Oe6UH)t~%Nj@<@lf4vxBH4E%;9jjdB5c8^?Hn_Kf2Qlbvm6t#=pc~?zLJCGz&C0 L)U(U~|2lsHLf+7d literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/id-refresh-sm.bmp b/src/windows/identity/ui/images/id-refresh-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..11d1893b4aaaf27f873feb2b411e9500fb8f9639 GIT binary patch literal 1014 zcmb`FT}TsP6owaF1Y!QQbZ$$BW|)OVrD*0~7NijALR`*elo{Ed)zqBJn28#iX_`8S zWm=;w6Di6dTZt}j)NKs4f>I)kC<=mZpSFqOun3_8?>XOr^StM~I6L<IS}Jfwlp}{Z z<n54`LrVlQN1llRPl3Rx*%%)m=YJ@_{0bS`tL!VC(dM)aQcvq#z3P0$KX=G4G!WBf zA<{5PxPBO4c><m(aja0cBTn8%{Qh|4xpFi*4K|x?f#2b(R#ZK2P;^g_)btF$6Ctdw zmLXJzBF>euPPv{n>a~QP3}bIj8e?N)ZXW7-1G@exG^R<S3XT)dAw}F4NI<m|QBpi+ zvl(T+5?N6Qvb<o_rG;+%rRxUn*uQXN^fO^+5)nQLK-ezAw<HMfI2k6B$+a^fGm#Wk zD(W(|8(-g|XK?BpT_f+=crcmZ1F=LL%_F+8k7z|EI}3LaQLqVVX%M0&DcNe38*jB* zscUFt<kJr#kL|{z&j(3K6v?M5=zi2gc;N<CKKFP1%fCiKXIG~i@4Nwv#X?<UGcj3v zS#I!Q**i}pwSmNFW05oj;nCxR*XvbmDc-t(b@KfKgQOQ<CdFt&R;t76TqwRbMfkOe ziPgrDTXqERZXt?`S%2hp^(|Blex&TxB!?cqW&15FIhsm_hKA<m)6S-2d|;gG=ihF5 zT@x=peCOfBSC^0T7P7T9ZnJi~-L0R`Kke=1<$#&t>7VFFrcvm7>9}|Quk-WS+4pVZ VT`I4&QdHYav!P>A?5xLP>Tik!xZMB% literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/id-refresh.bmp b/src/windows/identity/ui/images/id-refresh.bmp new file mode 100644 index 0000000000000000000000000000000000000000..6357fde4f1defcbde2a77393af3c4b5ffb7c7298 GIT binary patch literal 3186 zcmd^=Yfw~W7{_fYQCSvtSzveBg<W=)1;x0vuA-K@UWO4N3kF36xriEgM=`BYGaS)! zbX4S~f)^~ZaJ&>S5^}$zf_KSlrj?bK4}JS}b{!2SFr56-^v?Xx?K$W9o##34`M+aB zmWcXThf&xX1gQnN3VNlFM%X?XRpLH<EP6UFT)4phQM2YI6A~5Mt;SD{#Ng}yw^(e_ z;(ygEpRgFz+Zs>_Qf{rsc}pD*n`^NbG$1q?8@W9LqSsr`E!Qj>5*kBIO$`MF1yod2 zyfEK?>TLXSTJg<kVQ5whUb~vn?P#>VgN?y}ez7P0^-4sG^AK;Wr2mAO*vcKz`g))n zp-1PZLu>G0`poH6R#x_kZwilN{J}QN2ih33|12Z(&tTkr8m~<yi2U5ptr>)Nsh-z0 zYWfcHL@`B!VUagp$%FBpKLqbY9|p(!;1R4trB{=`FaIC8qa)*)_Vq;~1%)5JKv>~< zCKa@!o3#X|Sqiip^?0NW#BG5FO^OcPszE5;RpCBdgWEWFba4Zp@eOf<QF*J`y?gg_ zx#p+@l8ZY?`t~aGzPU>5vCB*^x`@PAcz&ypcbf<9TRhNg)}c;y=T(^)m!bZYmX?y2 zmq*~pK(rw`JePS{>o?5vLgS^Op`oEiZt$$RtgXDknu;5&F27F7$?u6R5av;k8?JlY zQRN8A)S%v>wes!E3KRjRF8KmSy@}g=EdxI=po!NZ*UQPw%ItDaXHN)=PGo1(9kw^# zVN3lFY^c7)_(iLbER!KWq(X5(jWWLzKSM)5t%w5!2cP9ED=S0wh8kD18sl5TC@Cr7 z#EBEn;+=Vx<H(o<zBvCghtB;({@Hu%YW@*bkPou2<Tw>6aop~L{X$2?!7BRus<H9$ zMDDF4V^@YXXIonvMMXt~hKJ%G<d0{d5cgDI8r`)Qow@HXTuf0*2c_Tt#<45+$*Me0 zKbbAkVQQR>24sE)0uom<JpBlMS<NUvJWP<;%%t#1c$y5Tjc%x>sFBTaMzL0H?Y}%q z$%c&^daSd$x|$hrb7=YT5mmPyaIpRon~qeGxNR?vfgae-m*8d|NARI`hV40xVPZJO zaYpRtNpUH3?Y94FlN(J<O+7L#HD~AK@=;nkXYW1c)Q#UUh6UpgCr4Z(Mcg1m6d}j7 zIuoCmRJLd3(AwIH-Z&6xt}{+YlsJFo`i$?A?MlGdfamg__Bkgvm+8^56xN(2e*Gud zt(IYXUV^AiP_2Z1J|d<@g;QQ$PH}ND<HIK4V3y!mDo6H>@a>N&tbFMLC(_c>dd0M` zTUuIJnzEcR6N3?H#n@yFpnr$px0f8SJtx5~-2r)o5|?l}wtF0~ua#k!CqYu~jI_+f z%6A>DqOq~D*UVF=PGOoHfqunabXzL085P89$pZVLh<?|_fANL-Z5terML9EM<PaPa zoUDBNZ)NzI{d&!_^eiwWlF9p<t@Fj=y@9!{$df+h)c_CrPOwF?LWX#i6h(*<-Do`- z*-!qL(CARapGmP>=fJwOk9*Brn6i@D`zlx<_+a9RE6g}@iIC6FF(U5_gEE>B$8IEL zMXJ^Bnwy)u<ZW+n$9IAs(l?~k*VlK6J)JMz?Sms`u(|HGRqLy6vi#&V5{_RXRPcP$ z-qW~mt7B4Be2@9+>gtG?5z%A3JLZ(A*%S(XWbvduXYL9<c$<~qT_g7BW$QXKG`j`U zJ2PJ>XLp>=*QUk9QUAk3YHvTJxZ_v$wcR7V_7)3EI(Sc5<BVDLghkJJsmz@jyV#MN z$IS<S(0Jz&MVEfD`hIHJHKK&I%{MZMqsNcGRHmiorOQ&t$lc57yN@|~`M!0%Nj-U$ wx96^6<=Wo==bii4o8!#vY@!q9GHp&0@64V@*vvQzzdZcX>+8+C{MYXJ6P@PDq5uE@ literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/id-sm.bmp b/src/windows/identity/ui/images/id-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..e507a62d5d0dcc258bed0913e5ff3f91f735de22 GIT binary patch literal 822 zcmbV~(G9~O3`8AufqrI$OppQk-<=qZEjmu}@X-j-I3hg(0`oXuq`cni!(1!t!hF^_ z+3a<z_d00({H4|*+RHe)J+1SE`u0^qW*Q@W&iNKkAGPSrOIKHy2VO1eHQ7?4f<}13 z`nZTc^0Y8dPI>w&YBFOl=L`SByAkFs9QlJeZsR-;${6#fFOg6eCd%`R9Tjrk{olmz D{&9b{ literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/id.bmp b/src/windows/identity/ui/images/id.bmp new file mode 100644 index 0000000000000000000000000000000000000000..72a93c6a947c4446c95f08f3bd3277e74225822a GIT binary patch literal 3186 zcmd^=Yfw~W7{}>*V@}ODkvcWWG=%7&A?`G0S(wg_uxp7fF0dB_VFF1Fg)U51V~$xC zA-CN{6eSZi60x))S%n3HU3M36xr$umVrqH$(6?XjIkq!S(=^kZ)0cW@p7VY<XXbaF z|NDRb@5^!99uARLjCh8Nnk#CysE0zH63_dqX+}thRKXPp1o%HzN=7zuM(aN+Yt~jp zS}o9<nOpc1b3{GyMH{pBw=#2I3y=IwMe59on2TStu*lv?l-<L^au3gzx|w&NUH*s1 zKKpC-e4R-VQ<IR&%IYp+k9M)*h@WMLeZ*9FWqw$3JE5ZYKliMZx!kac#BX{rh)Osa zKwop2)n9e<VwImI6`gY5lI{<3BjT0y8EZ@<``i#&-w%@c-5}O8{cNo5mDy7NnuA^< z%G}I;fUA_LP03)_r4io0IKsP)SIKR-BJ(YE{j3vxzgpeRQo#)`?cnjx@9P|)&_jCD zW{Nu|C~BWzf7>-aZXV-3!G81nklgd@Cjz`8&iXk=Cr=A+NuG{WsI9@A#_`KPbFAk& zhyBx(x+mG!I!=LegdD*&o$e$4c#rI@`9kAR(d+VcDT;jiM$T4hJwyC(<rmHj+?3hn zos)dhGRE5%uF7Y;;Z#8O^<ptob3Uxcn!QaCv$?sMq||gglXqzvyUp3bTU2*nr%34i z!KG1I<K%Dq&>s8aTvsLFa=8>SrM$P<9Te=|i+}na&XHd^)q4Y*=yi{4O!kyj_({@t zfjN(&a=T()@Q$%N9BedMsd4!^-G7q`@06_X&L4+KC_B&6I0Gd%hibea*W>Z9J$DBy zR%wY?6is+kG|N_M*^--&^e*_Fn!D5KM6+f+VLK}cD|8XMr-4V)Kjj6T0k_+&I<v8{ zk!Ydcnj>!6FH-hKi<y-2envqZk+C{_KA)=G9HF13>LOc24sJd>K$7rk+}Axkf5^u( zB^}Jn{F34_o2pFh`Xo6Eq&)bjX_Ore!=%*@pc9#|sr2$>VJoYRn^a}S8!gm`Jd!f$ zpl?d#;5hjghLPq(x@Q*JJZRP?tIA9?r_y%q4lUz%I5%{Qs;+7FiY$`yPs;k3@;3CQ zbXA$fC8Zp29AfnLZ?sR`rMB;eocnoVK8@mTi!x+US6{CxQ<}N$Z{<?rsKh&Uk2;YH w(tVTHFvv?8?~uQ1x9Ys$oR`^bn9?#xOwVGiB@=yeDm5og{%gD-_rI|J4i5k2Jpcdz literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/id.ico b/src/windows/identity/ui/images/id.ico new file mode 100644 index 0000000000000000000000000000000000000000..1f0f676ffd4aeee1c712027c9852986425d1430e GIT binary patch literal 2166 zcmeH{F>6#o5Xb))Pq^aHq)8R<yh2PpRVpQYAPA|`4Ss_(f>)&$gur$wRIsqgC%DSO zOr12QN)ZWyNq3;Q{$^i}1VQYy(D&Z#?)-0e=J#gzoz&BUMx#aL^=UzRK)=|zdU{*B zb4z-NHo_ibJ@sk1m^;66`qdIfllEvb$q~lQU^<=Br&8OaT62VPr<BeA>z}RwXW*}1 zdCv13kFcHwv~AG!^KozPPV#VLobT-NW{~H6`f;j@4{y}(_ca&{G#n1Kw6vt<<z=m` ztY|zQYjt&1TU%S&*x1ne`nuNE)--umwYgdKQb($vL+_8i=;-WBFW$Z*gJF&pEIQZF z49(CiHNK&o;^@x6(H-5<b6G(z=mn>s7j!xWdY}h-AOw2FulO}uW}`KN8-a{P#&_G& z(-;f}gTY`hG$a@d27|$1Fc=yp3<iV2U@#aA4IKu9!C){L3<j%ca2Onx9Q;Q>)hL|S zVR2X-miEf9ILVzvPEd!Xm4P3ffy3gkw6ehAa5x+ehr`iI1&71nFgOfZI)}qya2TTf zV?|d&@^m+TD{oc@DsmU*wNjJ2z*I<DNKjxZ<SzVJ$X&=?$lbQ1<whreA%7u%Zc!nB zp*97E0z;t=xouRXy*&y&ZEhnq2=WDN0Y{LpZEbQ;gMcHFgBNSt&7y!MU<n|CgzX5x z5-bcR1wz2WgAs>430NZQ(OB{{<SwAWP+_PrR2V7@6^3@4U<ep03>AiefiM{JVW==v z7%B{e#?WD?FjN>S0G0efu*$;9BqxUBI~=ve&N;68dadp4ZSCyrXm@v4dwY92JUrCF z!GVsCk9Bf#qSMn;ou8lU^72x%*-RH-t6uHz^Bqs9ZhiyV5wCd}H{JB4Cs$W<{(yg# vaGq`saP6mZ(`yTE2p#XW@H_wK^*7>Q+?9T1vwKq7_vg~LN8Na?rJwo_Usy*@ literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/ident.png b/src/windows/identity/ui/images/ident.png new file mode 100644 index 0000000000000000000000000000000000000000..904f0239bc7208ecdcbab6e99ad914ed84b24b28 GIT binary patch literal 423 zcmV;Y0a*TtP)<h;3K|Lk000e1NJLTq000mG000jN1^@s6?Q>5r00004b3#c}2nYxW zd<bNS0000PbVXQnQ*UN;cVTj60C#tHE@^ISb7Ns}WiD@WXPfRk8UO$RIY~r8RCt`F zlfO#>Q2@t3cZ!fhaD#`{5agmoSRgewL_<r9)DT29G)WK+4haqhIkbq9+SngZQ-Rno z@kpYj=^%(k^VT4O6JFDm;@K&F*T?6>=RJ6muIqv!CJzI52{cWkTrML-|4|(R!!QVi zLX--5Zf`1s3tTQ2;c%GD(IL|Th0|<?>cib&f>x^qKxBS~RD6T6kp{VwBwFp>Ss=^u zufW>ET!6$*1n*~+>~Wl@dabv>?RIwwtS!#^*<Tyw{V_*6mH1<v3aov7o19-Y@v2KC zV!QSNy8<Bu7Zshi55f9&8bwh$&N~F{j|lMm^2+6Pot4!n%h8~1S=SXz)1=X8k}sSw zITa*!khBGR78Hv`CdU12Ztdamc>aX}r1sBb7zUCg;q&<%LE8dVRr?05{Qw>@XRbF% RR73y(002ovPDHLkV1i0iuBiY3 literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/import-dis.bmp b/src/windows/identity/ui/images/import-dis.bmp new file mode 100644 index 0000000000000000000000000000000000000000..4257a1ad9ec59d426773ec0d2e6e8c81ea2e367f GIT binary patch literal 2430 zcmeH`F;2uV5JgSN4UiB@i*f+kNK`bGu5%Ndo3I6yFX24^Wu$G8auoAsCae(H*#v2+ z*0M*=Zv5u^&*bs><=W|ZAl|d?ShuVzcSCgUY5rdy=5RRAaOc4uE?hJA-+iE6hGB3` z`?tbY^%~dr!F8=~THpIk36{8TyB#z3L4<eSlb~Ol-~@O7v1i5*nb1+~HGyk<PI2J| zC%8F{NH)48NMn2*ErIwWv+Pfm%Ym4pL16*S8f61r5=0*Y=%VT}m!ffH4x2_mY;5ND zJZ~mNSL#{ia@pVGJ33F2V4Dx<<(hD<8PIMy0Er3Hm?yabK48=~AIt;}TLMG#C}_2! zrqt3@d}vy5S|3r(Ko{`{WzQ#5RJz19goq7aEusBu1tbc$Yl$^<SubLNqLFD$W^Rby zDVs&wWaZ{1S%sU-1Z`WbJH+E9C>m%|V){0{ybVayX^8;I{4tVz7n`}Y11o<o`>^ux N!fO|<nR7NDJ^}lAHRb>S literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/import-sm-dis.bmp b/src/windows/identity/ui/images/import-sm-dis.bmp new file mode 100644 index 0000000000000000000000000000000000000000..b9398c3849040fc6db484cecdb33f3d7faf5178f GIT binary patch literal 774 zcmaKqF;2ul3`GZ!XrZJ_i*f+kNc5Cb=O~k#um#mzV)g(O6p_*(%~58b{lh4Lz;e9K z?Em(+C(kdpH^J?R_{ek5bH{TP9*7})T>H<rux%Uf%d*5dg=vh#80X00KL-Bobi(tT zAciSMi1VLB;m@CEJg1C%J6J{xLu@4<+X>&wN(-@!Sl0ZyifB5`cbz1;WP!l|ZXPXx zWyBm)f<hOHGa!em34lc2-rJs3r}!Sg)AY9;DXkh&dce~Typ}^RaRHc^y0&T^(Su;= zkr)m|6opr#xTHR*;fE2J&t+XO>JU4aoU82n)L&LSeXYp%ufd6ipGKl66bDRAT+5Uy zt6!U>`ZD+Bvw0WeYxg~X>`fUK3Z;%86#%Q|qX97gVa4vNk3d2fbH7K5V|}qY@8Apc C>DlD~ literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/import-sm.bmp b/src/windows/identity/ui/images/import-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..0c9916877ce66ebd960598aee91e4b28414b3e52 GIT binary patch literal 774 zcmaixF;2uV5Jd-&XrZJ_i}nDtNc5Dha}?($*@DWK$R2^h5)G1%;(g=qm|cW~Wlya5 z{qgtA+xz{UwedoHW<9bVSU2`WH2b`5pR4e-k1&VR_ldbIi-`T??=Sqv@tC%hwp>g8 zR~TNMXI3Pu^R*2!aKya4#{V=weno>7hYBhCFh{8{oFYz=uHeYYEjMpY>Mn8)USwL+ z4_x{~1QTsKbbdOZy_hk?==qT}Cz4>J^K-N*AXg<7-A1X@%HfiADxQa6wLLZs3Gi?} zGQ+c{M_&0g8HJkM7(bO?l3%<YYjd_a0;t96nw%4dgbm4Uts#1wFxL2kGI#&Aj+VIx v<eR&7rsNRa7&*J7-J$#j$t)xfq-D@JsfH76uJ~G2s9s*q#-KGA*gJa$Rm968 literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/import.bmp b/src/windows/identity/ui/images/import.bmp new file mode 100644 index 0000000000000000000000000000000000000000..6a428c979ebb9fd4ca7c5b0fcc388cc88cc772f9 GIT binary patch literal 2430 zcmeH`F>1sx5Je|tZV(8;ZP){(T}YKCrE1?~<(qhmRJw%s5Q1$2K8o^2W4tbGg9Mvu zg*0f_=*`#H%;)#%S@QTGzO&v~udF9|A&PuW-}8?tT<<gFKG^GnYsUV&4_((;#ne6Q zre4>&%kPC*mtc?kb3TV5gdwIemL}Nd9vxg}0^KV&<v}KQbj*E!*138z`=F9D)Z$Dh z1$)=}E&4i_7YiN-f=LFU#35!+lxW(?UCcFN=)(;<PDUG)D9xde+Z@ocnBIA>dO>Fg z49&F)xviJRA;(JCqF#SY)P_RGXkW{?JP20iM+-9==<Mu{Qp>b*5zf#HTDg4?3Zqd& z_|x<nx{_;;CT*0~1G1L0?l5PG`{;GOh?PD$ukC;#nR_s$lfNnLx5gTmyCY{%H<dL; z=hYB2PUx(;Os_y>j)-;0WY8c+pB=DG6*r}20fD#;N}CI9ciLt>EQ!_o6@RN)%YzTD GxgmeD&mEos literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/khimaira-cfg.bmp b/src/windows/identity/ui/images/khimaira-cfg.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d20b9eb3d84f919e2f1b58c32d80043800dafa55 GIT binary patch literal 30056 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 literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/logo_shade.bmp b/src/windows/identity/ui/images/logo_shade.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2e36b9bf2016cf725357f87b9b43dcc9344137b4 GIT binary patch literal 30056 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 literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/main_app.ico b/src/windows/identity/ui/images/main_app.ico new file mode 100644 index 0000000000000000000000000000000000000000..8dcb29e7a66f097ad2f67b181bb352fb2421d3d9 GIT binary patch literal 25214 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�Jk?KB_&Y&gD^@AzC#JY_b4$ugpwja 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 Nl|lr5Zx?I${{ZwxG8zB? literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/main_app_old.ico b/src/windows/identity/ui/images/main_app_old.ico new file mode 100644 index 0000000000000000000000000000000000000000..e3222e7fddff3012958d7265d6c8848018b446b5 GIT binary patch literal 7854 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 U%`MvY<jtP+n-x}W;tJ*e0ncRMfdBvi literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tb-blank-small.bmp b/src/windows/identity/ui/images/tb-blank-small.bmp new file mode 100644 index 0000000000000000000000000000000000000000..0718d128bfc245c72044f42207c68ab69361dc78 GIT binary patch literal 774 scmZ?rWn*Rl12Z700mK48%n!tj3=%++f#CuZm_i}0kAl$<7)Bug0HIx8&j0`b literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tb-blank.bmp b/src/windows/identity/ui/images/tb-blank.bmp new file mode 100644 index 0000000000000000000000000000000000000000..01adca9e3d8b78b4beea0a9f9d29954b36234989 GIT binary patch literal 2430 zcmZ?rt>a_>12Z700mQOEEDgkr3=%++fx!bR59W*#qaiRF0;3@?8UmvsFd71*Aut*O H6omi)zup2D literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tb-space.bmp b/src/windows/identity/ui/images/tb-space.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d85cc5cd745cc44f4ace1de38f2552bf93294a7e GIT binary patch literal 2430 zcmZ?rt>a_>12Z700mQOEEDgkr3=%++fx!bR59UCL>$Cw3v@!PjsJW1U9Sw)ka6ryt aqv>HZJ&dLY<PaE556Gc0T0YP?JpcfUxVK9H literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/text1138.png b/src/windows/identity/ui/images/text1138.png new file mode 100644 index 0000000000000000000000000000000000000000..cf5ee376c3e5902f7648c53c64db63ff54244664 GIT binary patch literal 378 zcmV-=0fqjFP)<h;3K|Lk000e1NJLTq000O8000aK1^@s6U!MG(00004b3#c}2nYxW zd<bNS0000PbVXQnQ*UN;cVTj60C#tHE@^ISb7Ns}WiD@WXPfRk8UO$R3`s;mRCt_Y zoHuXYe_l>T25#1$3<8`#7@j?O!0`6{Cx-tV(hN2(;S4OILfj1BzPx96_2nDG^H;wZ z{{LcUkl^{kAj5rtVb9t}3@ohdd<=T(dJM?m!kNPipI-MdaDRHpz$CArz<>_8`Ggog ze|pcr^zS<Z6T0N@-@h4dom|Qw%JYNaBfAL$3$o<JGlv<TU0KCoEO3h9#7lh!tKeFM zf~~72GU)RjXZZN{JHs``cm|iaGzJzH7K8$BrVk7}Om7)jREikv>^<S8!rk!t{db15 z_gNV@xwuf=#CZMs_5T>&Kmu?pHY})N;Qs%Dfmg|g!NNWO(+ZX!j~QfGE-`%l_zJ}Y Y03JqGqf;=&8vp<R07*qoM6N<$g5zSNF#rGn literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tk-delete-dis-sm.bmp b/src/windows/identity/ui/images/tk-delete-dis-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..00adcf44365825b43f9c3dc27c2b4cbd7d07b7e0 GIT binary patch literal 1014 zcmd^6yJ`Ya5Z#bI$tR@r3(^P{K|#c|P*m(>Q4m+f!cM^liXbYA7K<+gABbq77Jh(R zh@e;~{z=-Coe5bq7O7HY;M~2#>^W!d%)9vUC~&-Kr$NJuhGz{A!mD<Eh5952gzvaq zF8C{Kw_9CXRaNZwdp-Znvrs6&@AqT5Tw=4?AQFi%4u`{y#_4pz@p#m6KA%Adg+j<= zG6;vme9z@_%-`*HH~f4)k3=E?S(YJ55>lxY*GbcCHY1zOg18oo1<`2qhD&d#)oP$# zsTK$X;C8!_PN&(+;czgQ`tW!>2nK_9{ARPs9+Sxg?RFd0YLz*5yPdx_n++zDi8Z}m zFVEt7c8a2)TrOk1USm3)qSx!8Uazyi)oSIQTrL-kMkAa~=beA2(?O%rz-qMu^+5be zrGjFyh-5N}SS$vg&j(Qy_1Rqe_xpX`>wG@PU@$;D9>;7p!*Dp{_fk&prv1@euDNul k{gOtv+eNF@!eX(|{~3)&D3waw3+-t<9_zKAI{!8Q1Q|N-3IG5A literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tk-delete-dis.bmp b/src/windows/identity/ui/images/tk-delete-dis.bmp new file mode 100644 index 0000000000000000000000000000000000000000..844fb23a647397b2a5c3bbe22814dbdc881366fd GIT binary patch literal 3186 zcmeHJM@yqo6wU01`2)kiY}66mh!{kygNo6_-WxWsw<K1?3W61^Sh0els3;<eh#~{l zg<Wvx&Ru@OnVdU(!TF+s;4qtf@bdCq-n-}CbI-lWPlNHZMjoy8^M`ePv#zh!^-1&H z`uXSi^+luMQl9(!dpOqnIJmpJ`zM?&EiEA{D+_A>mzS5&>-A`8X!uavm6a9fbUG9l z7b7(_6}Pvy;{W2}0ud1rFdB`>$jCrbQ`3jw9vvOQ$HxaPEiGtmZAEEmDH0PCaejV| z@bGZKqn*po&kuWhdmn;ZRaJ$|%uMmC;Cgs?z+^JnY>cC~xA#pv#u*e8gyiI8i90?% z9<i~ph>ng%U|^u#-P+n(IjiLKd=Ci;fmW-vyQA*KlamwV=H_B{b{6B~;}{$qL{Co- z+S}WuHbX;0c0OEPU8SCCJWo$giC>MOekW#rem)i#7cn|Iih+Rvba!_PCbi(}>nm~G z+}vP)e_wKRa&iLmeU8cc(jE{HU>D;{nABl&bF*kKJw09gl4J6lkdPoegoTA6G&B_M z?(To#6&4oSu*=HI1o!Id>P=kcSzcZ)d6CoA)m1DkEMR(iTJrAi??+cx7aAKILA}(~ z)!8suZ#OqL7z_rRjlQCyqQHJt=cl|AlYK=#$?MkE7S`6*gzK4^8H|mMiJiFq{{B$% zqV6IiBaxGnBmA>($#s2wJ*cI#v$HobtE;QUFa5J-+uPe%Utb4j&fMG_CMG68Es+P- z%iG%<X=!N^E9=6!<m~J$SS~IuI6gjp6_2?xPsTPfG9rAk_YV#Zu(Pv+jg1W~FE3+$ zejbyPlNcTzmfj;ToDICk-Yh66fR~pSf`fx!>@S5=WhW+UH8nLQb>tkQ?eOprySuyC z+}s3bUPniVU^2G8zCLtzcA~AVP3CK5Wu?q0>YrLue80BSKkGn@l9CdcrHnf-FAv$- z*`ig>Q1)w4QIXV;`lWByjdf+6Ie(}h&cWBP6gx5L&tkFQ>FEiNkB@kGc)<1bwd6?s zD`&Z%o}P+-_55HjvuF9+^8EZu-#PnYVq#!6n?(~96%`;3bB>FPlNqkQS5s3X8cIn? eK~hqZ^f1rT5Bo%Y-tqpv{~->(9DMyx4g3ZJvzf5~ literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tk-delete-sm.bmp b/src/windows/identity/ui/images/tk-delete-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..0ac2d6a8e9d6610a29b4fc04342bb094b7710b60 GIT binary patch literal 822 zcma))F;2uV5Jio+03}^ov<KK0iJsDRj^f-TTTsm<b`L;li3Z6>32){*CPGMHX(x{T z{-1AWUtZ7mDaM)l#P6a1?$Tqdr@pTj)sKBF%kpDo-}(KS=VGhfK5zdN=I0FjcDYcu z_w7$1Jg|6MtE*zLlIP6sF&3{Z%3rDMYU&~tm0dDR#Yu?`v-{h2t#*wr%}J^79K6CH zQ5YD<;vl0%nofDDc`_!QU#k_%rQ+{=b9VUqg*$9{Ds)Ez9It+{z|;=6iYGYcIWc$4 zW~#Rvk;tO~IqoM6m5N4Y#gouZu>6laLYE}qn&(Io;hk*VpU04de@{XlQbBgOeSbK8 wGQXVUVSLgnS5z@nlF>l^Iv`1O9P&uGHJ2QRc<qj}Gl`YLZXbN3__v7p0ssp0`Tzg` literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tk-delete.bmp b/src/windows/identity/ui/images/tk-delete.bmp new file mode 100644 index 0000000000000000000000000000000000000000..501ff6e241421f7c3a303d4fac59eed020a41acf GIT binary patch literal 2430 zcmeH{F;2uV5JgQ%Lyd$`TC@kCU5Sc@a_Za!=O)>LYA%tDM48eO%}PE>_~S8Q6*&f5 zq5xsZvq>C3|9x@t@br9XLc2%3L%&78LBBB9sKz{E{PjwExPbEZGwq+NUjLGZuIqHy zRBV}?2Wl<%d^{a17RYk<1nA4*fI3fe(E?v^0aY&dPUxZG3bjOg%J|vjVj3oEUbVpk zYigyVVic~D^i3|F7_@@*k(cMe@eopbAu1hf7jsnt&?*5e!3EJvWYg@v%caIq!<Z1c zy<^K{9r6m-3v<c4_2o)<Fal+2EdDjnfQD1qaDbvlsCQVqfQztXFh_9QqDuTl1w3fm zBC3`|UJQhQY6HQUtr$Rjg=*d!ZbTbE9kszgkSjzV^WrCi&g6{--~l#3j8Omif@EHe z1XnhL5eae-E4G9~KsB%gF3YPA3I_BEz=JL@dqJPKGOkh-8qpHmf)ZP^)3^Nmp4D-} c!#Iwsn<`Ed+-aIBK&vSU?&^-Z(_hDZ2bZ`e6aWAK literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tk-dis-sm.bmp b/src/windows/identity/ui/images/tk-dis-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..9100d9a9c7a568a03c58ff4b5590dc4eb8c89660 GIT binary patch literal 1014 zcmd^*yJ`Ya6h$YbPx1*V)7g!U2&S>p&O!tWQ4oA!F`}X%iXw=LqJ{WE@PUXHYT*y0 z6$BOkB<;%F33DNu9|$?H&$*AY@7?>nyFMQkBUC-V>3Grctm8p>)$@0JJSmER8Q1HT zzl`(w%<*`XxLhvM&*^lM=b}gS-g+&SN@$u!BoZMI2$0L=WRKtP$7Zuhh`w8Xv)QCv zE)$Ez2nK_AJRb7-JgHO)yWNhes#q)*W1UW?@A#cgheDyiVzHp#@6%{BC>Dz(lS#ti zFg~9Tr_+hmYUMY6x7(#wtFhnjSuU51Mk89S7Q&a%Abbf;4u^wGCUf`yU@)NFZnN9% z3=M)`uh*$mDrB=+;_*0MuNSx5{bM%f9LD1@hr@yOdd*}qA(2S1TCJE)r-VWwV=rnn r8a3u(^31y3ZV_(`heLY39-GZZYMjkxRI61(i|{j_&*h!T|L^q+$4mON literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tk-dis.bmp b/src/windows/identity/ui/images/tk-dis.bmp new file mode 100644 index 0000000000000000000000000000000000000000..558ba9064477a7038033d8ba7df65075db495d3a GIT binary patch literal 3186 zcmeHI$x35U5KZr!{(+{U7cR2Ug$o-Lafq`fCTg4q)Ci75jiQM-$9cpNN7N`Nii#ql zxQIxDC@usA!JRvIe!}UTYSO%P9%!$6lMD6U<-U9CRGm6?pUz<Wtda4<^VE9mo5#L- z?33oZ=lOg5{G!p&yyNNV2>}am5YWK;8o0Z=Lu_m;^78VKlam9p*^JE0Oqfh2#Kgp? z_EASi2O=XQ5gi@vXDKNus%Mn7xVgE3#bSZW<-+pvGG=FIF+M(yfq?-O7Z+o0ZqCmK zvC`Aik(88#`1p7qWBf}0YI9;%R#swXX9tUmi<p_2!PwZCV0LwNNj{9@@$nIEw;LrT zC5VfQLq<jhQd3g}k1^<UI+=eIv$nPt-QC?tOG`shQIYUWj>&I!cDC@4oSZD*i5VUq zj<B#WgocJ9BqRh{trkH+LH~qVUtceIk<){N18i?^V|{&H@}8ca#^~rM`uh67I<~a5 zfcvYes!&!|hJu0uiH*1k2??SNH9nPV<kn`h37_Qk^z;;mhlj%T#>NJgmX`kT-QVAj z&dyG>wzi_7p#jy^)hI777hZycgJCcjgkz<DwK*{vJMrkBH9I>y!_m<Zc6WENxw(mz zl@-y_#KZ&!2M47N4u?ZxWnD^3OND3Zo|;m_QLYiQt*s3U3k$;M_4PF_FE4R^evaeg zW9;qiVQXs(tE;P+pP$F%<RpfMhS1Z~Bj=i$nuKT8m$gnzO#Bcg`C40BlRDnt--B^n zU0vbg;sPfpC)nTL$MEp5U^2F;sVR(%jKJx1qP@Kxjg5_nh=>sVQ)|kc)aLZx+uJJ` zb#-+hSJX#EMTKaDakEd^uQfF_(wAJLZ?9&eqN3zJp|;pRYB<U@V$$E++Z$eAU-9zt zg6HRFJUl!|j#jJH$H(pMt@!`@{$MZL?RMz(dY}DDEbp9mAN#wxxmh$pJrajGXJuu9 t-@SXiZ|b(Nun_tA`N+-9l^*6^`Y{@fe*3-W0`K?#MFEZi9Q|(%`~nsFzajtt literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tk-new-dis-sm.bmp b/src/windows/identity/ui/images/tk-new-dis-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d2e1fcdc4eb0abcbfd3a0c2e22e1cf91ef18b7bf GIT binary patch literal 1014 zcmdT?J8J?#5WbK;$xldmom~_W3q`DgSO``Yq9Q&(!51iqPY^@|72hHVDx%;6w6Y7> zC<-e6leAxMH|*gVFh!~id^@|d^L;xrcbfOlM~>Q?u<`<41Uw6P;9iCG+diH+j)2+a za>0Mb>2$*3a1e1mpT&NT$D<f0IVAVC*K|6KKp+6O+YPJLidZZr=2$Eis8lKuB=44A zEEbW;WZ?CBVYAs_GMNyM#}N*Pp;oKG^E_lS8Qn)BksthWxr{_2f#Gn7X0wTCG>T+0 ziC{1Yr_%|gQVG3Y54l`UW6R}IqEV~Wkk99_TCFe|jcEO(2GU0;6r%5vvqGVOR;$H! zyOr=;trkk95*CXEt)bKDAe+rXr_;f1w<DEG!C)}ZI<9BK=FshSvD@u1nM}~@_2BdQ zFdmOlC=@W8%|OiIaKPj7(77-k^Yi%}M5EnqqtR$k4$(84&2)zKdL2fik!tk&eM#+1 z<JwQBQ~3RUY&IM0_j|ZpF3PD?D#+z>zw!rzfh31~52n9fuTia5#WRcF^W@*ScmLPB EFWo=ve*gdg literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tk-new-dis.bmp b/src/windows/identity/ui/images/tk-new-dis.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c6abc4ec57366faa57c41b232866c822a41de8d9 GIT binary patch literal 3186 zcmeHIM@ytZ5S_g*dlE0}f}-Gn8Dv2*jydOms2EXHRLqJABSypkhJ&I9MNu(ez=I+q zVnD<Qdh+JYpYV2GEhC@1GdsiT(T4uMp0B&>RlQeLqlv2cs*>?j_f+fV58eE(n_pDc zy65kq{iafJ>c{>4JwE3r2cJ1$ItR|r&*AUykJQvuBqt{$F)<PG@$ra_jfJ0|pTRn6 zYHEU)mlu3}egB$aVPOXED782_IYDl2E@o$EF*P-X(a}*13=E*Fs|y(!85kQI`^yHg zqNAe`5)y*Iz`!??{wwVpS`)LNpa2UC3z(Rgz{to5`uqC@v!$g);-McmH#gYd-$!O< zCISKi1jpOkTi*0RqtVFPT+HI)VzjolA|fIJ>FMdhGdU)|Nl8h<LuhEI>=V<&!vl79 zb~2ruoS;^#WzCq)#Vji;leoy~%E}7n=jSmqGb3>i4-ccSuMZs^9iWaiH8tRQ^2+$q z($d5?;syl;y<%QnT^Z$=+*VXn2%qG2YikRutE<8_<ENe8-d@@7?CeByb2DmdYY`b4 zDfV-6a)cL0M@K|OMTsAId3mU>um2||eJ9@J<fLe}v$KP>wKXg*F3Mf<n4h1I!NEav zcXx{padB~iNx!nPvf$?CCVE;}SYUj7+$bJ%ig7kHG{`yfd3=0~gM$O??(SlJeH}|n zOTw{_j}Kg2Tu@R{B05leS65e5R#r-l+1S{?*49?yG?c5#Z^R^D)6>(U<K^Y0_;Gl6 zh`qf%Y;JC1d3hN<Jv|_2mX?;ncT7x-#7tkTtgJw8ng7f+rF}zdVz#xlff$T|Too1; ziv8^DYy<}fOP#V_i;9Zi?(Pn)Rtx=je0+q1gM-A%+zJm5Hw%;YUS3}C{QQikrzbo- zJmC8JT4F3MEq!C7v9S>w8yk8^(SY^DTCS_BliV^CoAMibAM>HAs!H;=yu2L5Vay2$ z3E;l|Jo^SYWd3e%Z-cqU{Is{X2YVjvkaPX{_r6)5*4EabPjho~uf2%5&3bWmb{5^= zi^Kb8XJ^uXQc_ZIdV2aA18cgvy889^(b19A3iI(jxQxNm(^GQ#Pdn_D)KK~T?(R-B zV*giaqC8`2&HtK<i;FjQ^>?UgdwaXVJ?hTB%RJPd|A;qxFnf|QTer8jM)!=}G4*|D XXh{07sdFE>Prd$Evyb${TwA{Y*~5!3 literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tk-new-sm.bmp b/src/windows/identity/ui/images/tk-new-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..f2bf279539b83605d98fb1b5faa92781b5969af4 GIT binary patch literal 822 zcma))F>b>!3`JG+0v)?~EpmXY1-f_C?4!gtQHD;sgmQq40=g8?QPlTISq+MyC?P8{ z{r)FS=k5LY7%YzLFI-RKdI-;!597Jt`2M-awr#&M`;+^>aXnqWE+6OfKZS9Pz&T}9 zz4u)5-@^DRo&}#%?bj<Rf(%x1iMMS5lk|SPL@rf@hDbtg>Dg7hDb?x0{n@+mk~EPL z^p5A`RX0^^Ik>s|PZ&Ndhmd+GI$$1fD0*LbtyVE7rbGh=Mwl`H2Pb1gC>V0Ut;(Ee z#v|cSdTf;?2b_0*N&t?|!R)CT^JN*?-uO=*m7H0?tL>wTwM~^hrsS?a$=SIp_(sFL yTT8{NG}{?e6E!3zaQ`tMmw;(dKG<s1&o=m^v8a$snw?V)rwX804*qZP`FsO%kP#37 literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tk-new.bmp b/src/windows/identity/ui/images/tk-new.bmp new file mode 100644 index 0000000000000000000000000000000000000000..e0e7bc0d65a2405ec261660f64fedb1f7fce129e GIT binary patch literal 2430 zcmeH{F-`+95JgQ%Lyd$`T9gCOMxvsjbe)@!+{7uUe2GmY$}KI?Ao(cfjmI%a@vgFq zC_q?a%dzD*-`}%&czV7xx!p6~(QoND^b2#%Xv`z~uUFdh0>j(Sw12Mp`j>pzZns)B zGfHO62fN<v-}f)O-HZg|Tnp4Y4?U&iJAZzHQSM5(1YGYz>U-!kR_xl^g&px{kZV_# z!|H=d_FWL+1uhBriC0bfAeR^8*4$n7GN#NGbQGe1QzG|duH4rJWHM2)s<%Snn#J;5 zu5rn9asaUgMV-7GT!|PEORMOif3AiP0!4`PfS4%JMiK}VVu8j$?kU`uxuBVJ$;LoP zCX^oaB6*0YwzTwu%M>w0Bd>_eFM20)NhnGQpzAyq)!9~5AKM7Jp;x%+$OTQr;!Jf` z$<%nW7=&=TQ7Q6(5H%=_D71pk;AwhGJ*1-1N?~Zhg-rV#CUTvWL2(0m%yZ|>CIFJQ kSn5}Pde^$r@ZoSc)a}`m2Dk6~*|>VJ!L57WnfyBL2N-EX8vp<R literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tk-refresh-dis-sm.bmp b/src/windows/identity/ui/images/tk-refresh-dis-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2745af15100cfbfccc2099a25c12c08c1fccd889 GIT binary patch literal 1014 zcmd5)OUof(6gJI%W`4pfKYjpNnvE<(Aq&^!vOp=K+;3kJN=R}`xg??7QWi>$g@v*p z|75n$IA>!2O-;?I=Y8LL&w0-CyyyL){PkJDA%3iH4}5#z>jR$z-yiFr`1wU3AUxxK zzvEvZ9*^U6I{h`b*=&ZvV8CLrfW=~gUayDMYDF@c#O-!_@m{Z2TrQUv=x#orBNPfD zl}f>8v%%$Z!DKSQ@At#uaKLW2zv{EuECPW5+-^7LMx#*>27>{*-7cSnLLtOrF=R3s z^OBxIp}-vFavAUM@9=uP%&XJsxZh^8LA%|C$K!!WB!W~bMKBnIMxz0F%jI%hU#rzv z=Xg9utJUIuYPA}2xtu}y5{U!~g@WIKv{fn<(EHNd8jS{u#Ui%bEhdu*_f)A=I2;b_ z)#-FXsZ{cLx7%U8UL&1OW53^XJ!z~~t5~g8APwZ1yeyYXzK_LXFdB`>=kr{9JRT7a zhxv~B_j)~?&u7eLGxYm?_<TN0r&B~C5%ylM*HJ2!Si4rMWsXcH^Qxyl#L?+=&}=q& j-p~Iy91cPK6Nv<9PNUI?XGA`pIUn8WzkIajf6o66A1T;2 literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tk-refresh-dis.bmp b/src/windows/identity/ui/images/tk-refresh-dis.bmp new file mode 100644 index 0000000000000000000000000000000000000000..45f8099b9279359191b82f0bfb504313e66214aa GIT binary patch literal 3186 zcmeHJM@waC6m<5(^hy*1ilUek=B!8?+A&AWIR{h}MT{6*K`@{#V#JIYQEXIHYy?Fy zqPTI_JAXorb%yuwwX>S0XEAu;d)NEE@18oRs?K@({#o|Bnp}=ApC&I$_ho6ntl!l1 zUq1i7j{Z<n<5FCAcXxPSsvOMC&7r-${XgX;E-nswdU}|hoqdCQbaVvfbbNgLX76XT zGdDL!K|ujbO--@4w}<odb0j7v!rIyzHa0eJc6Nr9m6hxl6&2z1^z^6j=jZ3)>+6e} zni}Nf<iNth0?Z*aG!*&y`SM+6W+u|o(vXyt1RozC7#ka7ZEfve<1!9&Z*FczLqh|y zv$IiAQGxREas&qlOO1Yhen?MGSK`OV$HU0T2pbz4D!8nlIZ>;am>5JwMIj<00%2ic z@b>mrp0%~LMNdx;y1Tp4+1ZJ<wl<0V;ll@*n3%xH$w_M0)YO#snIG#<PEHoyudc3? zu})4-kdcvriHQje4-ccSuTSE(wzdjK{r&w|TwFv*NC>`s`GSFg0rd9vD)Gs&y}dnH zBbS$#muOmCTr4~!BqYGl&`@G1yhKduLoW^u4N1JNt}YO>zP=vr?(XpS_eV=h3nnKg zF*Y`ak&zK4KF?}sXu!qAMKH-3{p;Z108dX(1O^6TcXt<CTU#o1(u>sS)2C13L-I_H z$!~OYw0L1<Wd#ch3;6u`GqSR>;O6EAeSLjsX=y=6M@N>TPM+i6%!7V5Ffb6lU)Nbu zQX+LxtEHtS%*@P4J*>O3vJ&d*>e3JNDZLmF5CHmv9*vBQgq@w8V6%6*PaT*S>tIYi zkB*M2)LB+m269B5Ha9o1yu2)2kB^Vb+^MUpLrO}D^hRuKtaz)mv=n)Hc}Pu7g|4nH zs;jFpJw2_$KXa!3^xyUMwF)L{As#)#UMKgftE*s764%kuQS#=j;hb@Gb(Lr4N?oW0 zai^xH#4FTOf#1>5AzUbU<((d4pAQZW3ZLJ;eZ&6#KDM{FrJtz{dxtq)US7)VAs1$5 zW(W@tms-g$eZ+IzTVG!X^{3zITl(%tOmauQspG}Ph2(K?aDbhi9qDK0O@4U4zrV-r z?JdsE&LmfQnwYHd@bFMP{q^ft$(i`<qnn$XH<(RLO&~^LVWDu9o0|*89Q*k3qx32J z#?sOf%#XN_kB>@x)=xa1)z;QVZEdYQv$r^BUe~DLmc97={EYA4zvJoY2@eks;N0NM zVs9(n;dhOVjWT!0D`y`y_3-czU(;vox1gXP;f;MyKPcW;+&TMRzfV6Bhc%P4fBJ2x ws;ZJb_8{jzb>vyb;mjxRoF&xbM_v`Uzv50!*-O77_Ipg#|E1pf`2X_p7kq&=RR910 literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tk-refresh-sm.bmp b/src/windows/identity/ui/images/tk-refresh-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..acf33618d2e750de0358627122f6e15031e46ef8 GIT binary patch literal 822 zcma)4Ic~%-3{;R0q)fMMpa*QbK>C!d{S^5p+9H)cp?yF~18D;IDas5-QWk-WC1m>G z3^^qI{&9W|ZfE#6Trd543a|b>^tfK+zlW{s`tQq#9N%rRQp@e<_8^h?R7xhL?G4_N zg;PY)!veqqBmC#}+UnNa@N5^uxo6oDZAl2}l)SOZ7psT`?-i3+C<@uiuM(Z&!bsU! z4jLwYWTt+do_|tWLp#8S6nQee`LkvAf}HM|UdDk7lOyzCp6o80G6+l$+c(WhnKgWX zOJz<>5-iK9gP~+hRWeFSK%&!Du>w+5_W<vLfkbjdu`+n!L6MRvUd^IM04T<nOI9uF z0!X|Hua&7aM5UZjwB{{?rLo`V@AJ%@B--T9<`Y8(!KR-r|E*wbfkmgCBLAPU*<VT3 B;#dFx literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tk-refresh.bmp b/src/windows/identity/ui/images/tk-refresh.bmp new file mode 100644 index 0000000000000000000000000000000000000000..6e39f877486cd850d9431ef8889d9915a576fb75 GIT binary patch literal 2430 zcmeH{F;2uV5Jf{tLyd&cwkQXnMWUjioH{qbxrw)+@+Gp7C{sqFSt&;`f99Jgh2v#| zC_q^Dtaqb*{_|(z$LE*J%Emq79o8+@4c0|<jaXGrc>Z?9Wj`Ri|IGO3($~M_L)*6O znhh;e@<AF2T{s*M8xA<dbs=~~$0;I;lWJFgrRz^{!6Nr{zmMG*ZD21Cf?4oql`Bmx zjRS%+_)%34s+x2}%fMynt6U@tTd9|l6WR<>^rZ4pgR{AxO4VP^q<2Qg2)obVR<73R zHHz)U3((MLQRQ;1)iC2jV)+_ApwF~QUU(9r8ZoA^&>ML!l(KmN4V`L4hk-8MB`6#y z-my8Zr5~IF5fzIaT3FG{;~QKv6hw#xCAd^KT0sZO=Ax20y;ds_)dYLll->op4B{-g z@M`3_nzF~`py?|GC9=|i&bHm6SF?Z>D4hxyy>u)CoJ8Cl3#H5GWuMbc9+39HaO`Tm kA6DM9^ml$NW8e47uiN$+y~8kUV=e!c;VvI<-v2u82jzJbasU7T literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tk-sm.bmp b/src/windows/identity/ui/images/tk-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c89285c610020899f23ec1e486f0a6b7ea1370fc GIT binary patch literal 1014 zcmd^*%S#(k6viX2ZHpjuqjce-;6muaon5$aQBV*S1VM{f6f}xjTo}<>A8{IRZ1ZXp z^P2IIsHw)Wf@q;qF_fr85)E2v5ftjet=nHG%FLqiA1HqCJD1CMzH_*@tl~P`aHA5n zK2>?7@=ztq@I<Y*+J0a#q|ua0rTCvoCX>YDah>#Cd(XdKB9YKNt!GrU(Pj_QD9%zV z$aIgr*LxaF0{NW|9vkg9yc)3Ro|d0<c^M3TrCF4z6a18S4$(9If%cI}id%;%Fj~oN z5y>$v(Q3A5<ZV+v2HYWh2Y(QMTrjwGM#uUu8di>}oIj*gI-tlNCC})*Gylos$09AF zoLnI%F0sZgc>Dc~?u}ELloP5Jj(F~kQTW<LyWq&!@9_t4%q+0F|C8xx3g^}#=Cxh= zR${b$-lbmN;e~UPXC^OQmX8^ENs>7FeNN<C6nTA{t~X}p7sE`?g?QQjjwaU^YHSgz uXZPrx$jmE>LVAWTAmR1}h$yT2j$n8h!Qo-l=4Qz1Mhb;>Py7G-e*OhgI1!5g literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/tk.bmp b/src/windows/identity/ui/images/tk.bmp new file mode 100644 index 0000000000000000000000000000000000000000..e689cb830bde150c95e6390d225620fe65ce655e GIT binary patch literal 3186 zcmeH|T};zw6vo-rZk+qfb^%M=k}YIrUu=O*F}UIQ66gx@VG(Q#<*O~Uw0{`!bB;|E zWQcP_Hz9(e;uMr2O2=T+jxnWBD3l_gAR;O;A=#E?H^05I?1Gdf-OaB0CeM5E=Hxs% z=XuU~-;XI+=Cg1D#qBTfynwI3BR(s{{r9=D+{fo3{y9#k6ED0y@Or>A53C9eCpKNr zdbN%SrH&6aYY9!$5)cxG!{Kn7rLxK@{G$}CjMum!C{pegzmyL0%zR3Jy2XwkCMoQ= zf&S7s8Lgv4?mfk>(lWOktK}*9?eF2~il2GByn7K|-;n7RzZ7RemVx~}Q+#Y0qWHof zhVyo^&-S5g>?JTdkv;n=vD@u*b#<ZA8t_+d<E4sD{PuS6%x4yse<HBEYk}KS&W*W7 zw$!!qZfr6#!%iagJ6NaPPDsvHLh?$8E^lOYawb74Gr?6BUQkH*C7F3H!NgPX1w1A< z@aWo<|HPT9Euf^Kjl9omsqUSjqGy^<uS~JE>n5h}Cdh3YL)9`$a?=R1#$m!523b>Q z=j|hXtUT1qiwCc=EHsTLM4vsaQ>8aie)<y9fuqz7+~tt%4wXH(*=@bWcA>G<F@f&l z80p`RqG%pLetMXQ6AnV^2MDa`=heeDo($WJuh9671kd!;<eNFzWMNx%J!hQvIW~Bg zulnv#^~-Jc2ygBDagw6%Z;&UvrD?s+re+uM-wYFV(!qPj26(5opJyYKyj*>aw-U2F z@=%UZqNb&TlEcTjIR1d9kvSS1_c+o&%NJK?DC?eP*X2oy1+SrfoU9AiNjv8v;f#~$ zQ$vIvAH;XPnm50;5tN+m5vSf%NJCpE<uxa`JT*`I!~@Q`<~TWYk6QaIUtXKRRNqc< zOCMW0Z&F~HAV+AdJntgutdrQrAy&kw2|j9PmBKSlqf~@-&_%k=fKq2<Q?7yJYy%s_ z+y+N|NTO0hY1uxs1!lq%QwfMs5Gd*i5Pf`2%;PI@8iJ$)gf}=?lak|6ol?<9-2d%Q zrsn^^HTOIHv%k_kJx{Z3oNRsmq8v7xjYx%hVSh*q`(um@;jI+mtvI3a+SJ@d@k{N4 zhi@{O46{^-(JYZ~lF%8=<QPoEC2hoNwYudf5xXfaLx)_GhfM6uXtkb5m5wl_mUZ$Z WxA=eK^}c)dH+lQy?bHA1fxiH2|MYzT literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/vw-refresh-sm.bmp b/src/windows/identity/ui/images/vw-refresh-sm.bmp new file mode 100644 index 0000000000000000000000000000000000000000..aabd10ef575a242c200726eb6ecfad6143b86d47 GIT binary patch literal 822 zcmb7BJ8r`;3{}w!WbEd(@By+0=$?_YkD}bf88Yz_&H*}VEa0Ok@A1hWG)qFz1Vz0^ zKG8qt<7WyxB7WmLwC^K*MLo599k$r@*7YfziA>4AV_enO%tm9+lmPd^Y_QADaHO9U zdnABqG7`&K*fr5H`s5(?#4r6Vn~x66K7tF^Xi!@JjzmX&bouRim5WOXh);Taa-T{C zYbwlx&WQlW)0&>vA27fF-QgAZu^?F|QrM~|_5=O(w-P)v%Arb<FlEC>e^dm<&^i~s zXj(_K)ltv#zeFcEj9jXsNG%PLaI@_b@@6(fQG*cSo{1+#_@zH80;4S_%mw0!$D)F= b>DPM){u&fPRAkd7S7on<Jzp@r_pSH<s9U1D literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/vw-refresh.bmp b/src/windows/identity/ui/images/vw-refresh.bmp new file mode 100644 index 0000000000000000000000000000000000000000..76b68ddf758d97b377305f789cd1469cd8150d39 GIT binary patch literal 2430 zcmeHHJ5I$g5DX;^H4;K;Q4T<hL`6gCIyb@GB(I?ICGsLsrnE%!q#VV}>_pKkTx6d_ z0b;dY`8zuvZ}R;5cHNjgB0iwqqurrhHMfXO^Mduy8#S9i`1%?9$Ca*sKnGSz+l976 zTstZr3~fmxu9{T~s-gonmwgt70CXR2>O<(m`<Y=#e^TtwL3WVv;f5H6xpg$Oe9;ng z#P#wGF{B{@>pUU{Tvq|KlW$8R3Fn6-|2Q1v%0Xx(xLAiYrZnaP^f9w<X?O#Gn)FT` zR=$8s$I}sBfIDP#wi{3ow+>ck(6`KmpbMiMYDfyO_Tw5~oD?W7Tb4}4$Bpt@D%8PU zXLe13nlT5}pg1ZLNP!i9odI_tK^zuwM0kqCCA4t8d~s5sph?C{hPcE<VHmVuU%gw; zPeApgXe{|$=qO&I13B(6-@9S=e%t%snWm|_)iqi9%gyubf~an;;8yo}Cx0F8H)KcD A#sB~S literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/wdg_collapsed.bmp b/src/windows/identity/ui/images/wdg_collapsed.bmp new file mode 100644 index 0000000000000000000000000000000000000000..f4d82977cabc95bd7750d96ca63208ee86c83c58 GIT binary patch literal 1014 zcmciAKTE?v7zXf&yZR9v-Q4^DT}503se*MdLR+a-&_#;Hp;#z^o>3!7X$s=l`e#t$ zASsfmU_)qQg@8dyo5iIdI6bD78wyby5np)jrr&cnyn&gwVWu}z?m6LR%+08qA!gh? zKj(ggVY;DXnkN1#s;Z(~F8{humSwOki^Ogcx~}`~$>nmWR4S-GJtMNY1;X>bIz&<I zz1!-%<DuOFDJocCBPg9;4AdhC0&2AysK2pZk*~H8+9OzstV7eZZ+m$6zl}zt&&zrn z``QcEt_%d&ZNztyFbu=9hY$kGvON99t%YFe5tFM)%+4=*dXD2@+qS0<a%Y$hg(2n( zp0>L~nM?-FX0y*H3YVB$iQ-t4zI1f|(&;oD$Laag>pLumV>l8{est8QQYk2kf@my` Y1O9MeN4+FT$Y!&s*XzD5?fCEe1BcZt8vp<R literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/wdg_collapsed_hi.bmp b/src/windows/identity/ui/images/wdg_collapsed_hi.bmp new file mode 100644 index 0000000000000000000000000000000000000000..90ab26d679f1448d9687ce4b0c7fe0b8ad004caa GIT binary patch literal 1014 zcmZ?r{l?4y24+A~1BeBHm>-B485Dpd1RUXGU;tqlxPJZme;A%IYxe)MXV1d<FmYmO zBcF)>O5Sn*t6Mw&U%PgVWOH=9BL4HQc>kZTdfk6rAbauh6(pIV?FrEjHe=bF|3Zmv z{~f~<{~tJTfGBe`k@SPi0Gl)C{(njTjQ?@DCI2s6xPaFj4UcfJ9++Nany2gNe<io@ z|1CWeaGIg!9)?pt%q-r*1^@LuL;tT{yAHz~6*t2A!G2o&;=hnb!vFC2WE3-$T|)`m zKjqSY#lZCckr{daPo6x9qF)J*e%@)<{>ukv{|`yY`G4%#F%*5U^rheuf?+@J%v=BE zLi7IzBxe0Ta^whBeK7s<&ItXybMF0@i75H+9iQ?4;K74<^}+NjyGQ=#oA=<qR8+-( m_vqCB`}glBN*_$WimA<i=g5@*yLay)NgquAp+kpA)&~ISI@*H( literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/wdg_credtype.bmp b/src/windows/identity/ui/images/wdg_credtype.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3bac3fe5356ebaa52ba3b32cad80f2f95cde1d3a GIT binary patch literal 1014 zcmZ?r{l?4y24+A~1BeBHm>-B485Dpd1RUXGU;tqlxPJZm|G|bYUcC6fy1wCmWnIJn z^4f;~eUqk;<i?XHPySCXZ~WhP;O778?N|PXW|xwzKeN8`|BSQG|2H3g^gptoYS`;f zNJ#i!R$2AGu&nZbMnTE{>gh}1{`bkP{U4BC_}@FF;J<5P{{Q&AGK}zOZ*Twq>g%un zS6_eqfAHal|7)(lhU?GUdG~+Rh8zD~mtFpEIOp7d_jJtoXliPrNPkI5$^TklnyIL* x|6f{N|G%)Z_J2-!&Hs$js{g6QmH!h9EB?pkm;a9jy1k~o2gCh?Ee{au9stj97JUE! literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/wdg_expanded.bmp b/src/windows/identity/ui/images/wdg_expanded.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c59072230989db705691a25cd94c51a23fd1bf7f GIT binary patch literal 1014 zcmZ?r{l?4y24+A~1BeBHm>-B485Dpd1RUXGU;tqlxPJZme_G<pmoNXncI_J3b`;h& z|8MD?NS6Lnr%wI1N-F*DlvGHT8xrzL{)a6;``>Tsw*PG;x?#u8o&SR>d;S;gz6Zws zsRjS9UcE}RKcX}8{&yaI_`mD;V=yjRzVClW-vpxc&tJ6of90%I{}*0*iGpLYiwL>_ z<i@1j!v8z&zQe#Xwjcf9-Pe!T4V^u`|2G~v_y59+FIaJUK5jRhJ$v?lMQ#26=bwM# z#QRR1{omW$ht&-Q1qJ_?EnD_~_wL>Q_wV2T|M20%|BoL({vVVM&Ye5=|Ki1q|F2xR z@;^R49!#UBdyu|On>PJlzkdDyHEY)VU$J7v|D{Wp{$I3c(f|4L=l`EGXU_kbGiUyv OHf<W%OmzJN!3O|%G%RTV literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/wdg_expanded_hi.bmp b/src/windows/identity/ui/images/wdg_expanded_hi.bmp new file mode 100644 index 0000000000000000000000000000000000000000..6b2affc6dfc261ff6ded5a5610a6a6fc8d4d5b67 GIT binary patch literal 1014 zcmZ?r{l?4y24+A~1BeBHm>-B485Dpd1RUXGU;tqlxPJZme|-4j#f$&1UcHJ}4L<#` zIYs~L+PlcozkBzd{{o)L|0QgE{)60s&mL@Y#@=E7`Q|<N&(pl?e`aYlN&34dPy5fC zG3h`5ig#cv?il+2^y$+?nQ{L7`Tuf`fpEPbGkE$>{|`&cB}#uNP#sUtF%<nEGekWT z{vSMe5U&}Vw`~0{8kmcr7o?we#*P2(k%@Tq8+Zo)=Uea?tA3Cf!om6fw{G2r)r|V~ z?*IIS^Kj|~>F1mO@V}*hB!>P|r%wG>bPLC;7o;DA1>+k3FJ8I~#SGur)c?E_&k?1c zZ^_I5`tHGS{j1ij{V(Dk_n&9N+5fzgFaGD9b_Ez#*Z=d*y8WMT?tNf*Jo?YK=-Gde zKECB|!FCAe&G_F3Oe3IlH-F)x|GAa*;53$6T=_pCzwCc(Zt?%f%!2=*xC~6r`tO&J g@!u;Z<-dDW(tlTA9Jquh{?99`g1Z4*`XxpT007d}8~^|S literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/wdg_flag.bmp b/src/windows/identity/ui/images/wdg_flag.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c563c56e9f04b8e5cd118bcc4a2728b9d28099f8 GIT binary patch literal 1014 zcmbu6-77<39LFd3_!FeWQbO4-l$c9K@^Ygn%;_u+I~hY>c7e@}Q?eOxv$dv~rLoMi zE2osXn7t)$7mA`xDK5XxIXbI}spD7Q=lAqH&*%I6e$S&)zvs$?P;g@@rvgs-oN{D^ z-1rqIc`_L<F_Ot7{*u$_{PRpU50WGikH;l@xLht+EEdT;UmHOX|Kd$16a0R^WRFNB z0*ay}^L!69O(Pr*OZITP-RRd0WaSS70ijR6Uhz+((I{4|HmJG?6jxNI*TsE4vn-2| zQ9X=|6-Ju_#PkAsMrY7LO`=gdfi`9r?Vcp$>TWO$li|VNMGjJkKcsN>@P?z?6t)x3 zSUZ2jB72Xq<2z^$t^~f;#-dVLlfj$KW?Wp};QHkQr}wY$Uq1cHH#njwQr06F3<@56 zZw7+_F3%ol!xVJ-aZJw6VT7A&^;761Y4qCm)A#edT)%;)mR5nc+wIWlbntq;88wM- ulSm}6yt0bsz9H1nOHj=3plo;<CFNC!#bQ4)BKtjuLLsbgZo%jCrMXXo2o3T8 literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/wgt_arrow_collapse.ico b/src/windows/identity/ui/images/wgt_arrow_collapse.ico new file mode 100644 index 0000000000000000000000000000000000000000..b248bd9d6c71a961ba093afb4c8fce91b3bba4e3 GIT binary patch literal 1406 zcma)+J!@1^5Qg7C#3ckwnymtADpOCDN=P3Ffz;^+|3MnTtx^j@;B*!$SlHwjY-M4l zP8tJ3ia-!dx&_7YIkQ<~QmA|Pe9U{#nfJMK?yfY)wX~%AJi8|inLVL(=@D&C1Nxdw z$Ju;t7LFvo8jYp{JyVUxV~szLb@}O?=I7@%7!0(yxTxjjWetZzt*oqQb#+y1Yirus z+0o|arZzS<w7$Ns(eu>SR_c|Gm0v?2kH6{o{9G^Jf56CSPHVK7hK6QnhGt#M=@LhG zbVqk|NAKGjdP8pn4ZWc=D9{5v&;udRlQ@Yp%Gu}*e<P8x$&@O4I)lMrFc=JmLW03y zFc=I5gP|~CFc=I5gTY`ZbQlZ<gTY`h7%b7?FgUDpWL5=G82i;|K|}A442xs#7&%ED zmMQ}`or%NZuvA&#a5x+ehr{8hQo-RcI1CO$FP+2TFgOf>SJGDr@tI!4Ro-45sPJyA zs#4?KU}~5)3>r)g@5aqWl7@G~yN;v0(eZEiH~jk%HT)a3X)rVx8g=N$Mpf$RQ5fm_ zF;auTFJKEe0>3)i&Or?Vj_4dbSRFT;0+xU!fCvoh6@Vq!7%U2efQ2uOJe*0u61^V9 z(%*)D3TQAS3<*QRkT4_+^*X^2FeD5KL%={98tY+57!rnrfz&i~7!rnrApuDIgJj9Z zWRa7@i5-rt+<lGfvEFKTcUOCRd)nXM*TKPoj*gCWczCFjlM|hup6cxEOcxgyy1Kg3 zWHQm^_tcx$ulXI1sBZra^nrX8^}IbyE&R_%cb5G--oBMq+uOGU!as!yZ-nb`Cj1U( w!UB)L=f>eqSod%4{(UyBLzU*bCelx8^W%jyc`98#l)gORzwrL;HM4!c0D+9I&;S4c literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/images/wgt_arrow_expand.ico b/src/windows/identity/ui/images/wgt_arrow_expand.ico new file mode 100644 index 0000000000000000000000000000000000000000..485b537b03ce7758cc82dd275e16d3b2cc921898 GIT binary patch literal 1406 zcmbW0F-u%g5QUFmU<(Uvwi1$9#d<2M!1jU&)^<bwflVM=SqB2b?JSZ&iZuR$ty09P zwTYF5Ac1JNA;sjo^CGN^Rd#pYy>s5ZbG|qCt+c{-eO>i=^Gq5udqs5V1+k(L*P0CD z!+hl-ol5*Poel$6rkc%Wn*EsR?)zu0uC8h{8fk59O&c2<8jr`?+}zaG)|R%nw{>uE zpxxbFO(qlV?CfazHnq2x`k)Kt&(ODvU%I%t(fco7F*3r5M#PXBnxPq*buEV_j_&A= z?&yx*w>9*J-bfmHLuXK+2YR3fLZBykl4q2&aW(vnLdGUjs_eNK3<iV2U@#OC3<iV2 zU@#aAg$aYfU@#aA21B94U@#aA27|$1i3W$kVVxthI)K91zl{h2y+1N6j=5vx6m?jt z4cv4l4vWK5ZGpq#a5x+ehof2rhr{47I1IgY4u`|wFa%!7wOWYJ@FlPI_U=H1cVktx z8t(>E!?a=0U}|_bZZ?WEyc^zi9OaFUf5X4w-;b!_-{?()p~2ATLq9gUQcsV<$h99M zJqY{)wtyq>tE254^dR7f&cTD#akD9430MM%z_4BcSb~kgqCf~(_z~3MOahkZ{V10H zHuO_KgCSu^7!rnrAz`T335I|nVMrJP2Fehuhaq7|7!n3b6X-A`3<*O5koX71l8wnC zr-qX|99gyd8rMsG(&6Euj*gCWe0;2vlM|hvpX==GOqZ9Jy1Kg3_4T!GZ*O&ff3Nv` zuDd^}kEf^nkEe9E{|EX&y~=vsp7!iNKKo01_LqeHC4v0Wik>eDl`jZS-CwkPyjyxy mjD-uO7Pmb$ANo+O5iQeiV&3B&@tS!4Z|r#v_xrkseSZN6XQ%@J literal 0 HcmV?d00001 diff --git a/src/windows/identity/ui/khmapp.h b/src/windows/identity/ui/khmapp.h new file mode 100644 index 000000000..bd53bde28 --- /dev/null +++ b/src/windows/identity/ui/khmapp.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KHIMAIRA_H +#define __KHIMAIRA_KHIMAIRA_H + +#include<windows.h> +#include<windowsx.h> +#include<strsafe.h> +#include<commctrl.h> + +#define KHERR_HMODULE khm_hInstance +#define KHERR_FACILITY khm_facility +#define KHERR_FACILITY_ID 3 + +#include<khdefs.h> +#include<khlist.h> +#include<kherror.h> +#include<kconfig.h> +#include<kcreddb.h> +#include<kmq.h> +#include<khmsgtypes.h> +#include<kmm.h> +#include<khhelp.h> +#include<khuidefs.h> + +#include<resource.h> +#include<credfuncs.h> +#include<appglobal.h> +#include<mainwnd.h> +#include<mainmenu.h> +#include<toolbar.h> +#include<statusbar.h> +#include<credwnd.h> +#include<htwnd.h> +#include<passwnd.h> +#include<newcredwnd.h> +#include<propertywnd.h> +#include<configwnd.h> +#include<aboutwnd.h> + +#include<reqdaemon.h> +#include<notifier.h> +#include<timer.h> + +#endif diff --git a/src/windows/identity/ui/lang/en_us/khapp.rc b/src/windows/identity/ui/lang/en_us/khapp.rc new file mode 100644 index 000000000..a1b03b411 --- /dev/null +++ b/src/windows/identity/ui/lang/en_us/khapp.rc @@ -0,0 +1,728 @@ +// Microsoft Visual C++ generated resource script. +// +#include "..\..\resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" +#include <khimaira_version.h> +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "..\\..\\resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "#include <khimaira_version.h>\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_MAIN_APP ICON "..\\..\\images\\main_app.ico" +IDI_WGT_COLLAPSE ICON "..\\..\\images\\wgt_arrow_collapse.ico" +IDI_WGT_EXPAND ICON "..\\..\\images\\wgt_arrow_expand.ico" +IDI_ENABLED ICON "..\\..\\images\\enabled.ico" +IDI_DISABLED ICON "..\\..\\images\\disabled.ico" +IDI_NOTIFY_NONE ICON "..\\..\\images\\app_notify_none.ico" +IDI_NOTIFY_INFO ICON "..\\..\\images\\app_notify_info.ico" +IDI_NOTIFY_WARN ICON "..\\..\\images\\app_notify_warn.ico" +IDI_NOTIFY_ERROR ICON "..\\..\\images\\app_notify_error.ico" +IDI_CFG_DEFAULT ICON "..\\..\\images\\cfg_default.ico" +IDI_CFG_MODIFIED ICON "..\\..\\images\\cfg_mod.ico" +IDI_CFG_APPLIED ICON "..\\..\\images\\cfg_applied.ico" +IDI_CFG_DELETED ICON "..\\..\\images\\cfg_deleted.ico" +IDI_ID ICON "..\\..\\images\\id.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,1,1,0 + PRODUCTVERSION 0,1,1,0 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x0L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Massachusetts Institute of Technology" + VALUE "FileDescription", "Network Identity Manager" + VALUE "FileVersion", "0.1.1.0" + VALUE "InternalName", "NetIDMgr" + VALUE "LegalCopyright", "Copyright (C) 2005 Massachusetts Institute of Technology" + VALUE "OriginalFilename", "netidmgr.exe" + VALUE "ProductName", "NetIDMgr" + VALUE "ProductVersion", "0.1.1.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_TK_REFRESH BITMAP "..\\..\\images\\tk-refresh.bmp" +IDB_ID BITMAP "..\\..\\images\\id.bmp" +IDB_ID_DELETE BITMAP "..\\..\\images\\id-delete.bmp" +IDB_ID_NEW BITMAP "..\\..\\images\\id-new.bmp" +IDB_ID_REFRESH BITMAP "..\\..\\images\\id-refresh.bmp" +IDB_TK BITMAP "..\\..\\images\\tk.bmp" +IDB_TK_DELETE BITMAP "..\\..\\images\\tk-delete.bmp" +IDB_TK_NEW BITMAP "..\\..\\images\\tk-new.bmp" +IDB_VW_REFRESH_SM BITMAP "..\\..\\images\\vw-refresh-sm.bmp" +IDB_TB_BLANK BITMAP "..\\..\\images\\tb-blank.bmp" +IDB_TB_BLANK_SM BITMAP "..\\..\\images\\tb-blank-small.bmp" +IDB_VW_REFRESH BITMAP "..\\..\\images\\vw-refresh.bmp" +IDB_ID_DELETE_DIS BITMAP "..\\..\\images\\id-delete-dis.bmp" +IDB_ID_DELETE_DIS_SM BITMAP "..\\..\\images\\id-delete-dis-sm.bmp" +IDB_ID_DELETE_SM BITMAP "..\\..\\images\\id-delete-sm.bmp" +IDB_ID_DIS BITMAP "..\\..\\images\\id-dis.bmp" +IDB_ID_DIS_SM BITMAP "..\\..\\images\\id-dis-sm.bmp" +IDB_ID_NEW_DIS BITMAP "..\\..\\images\\id-new-dis.bmp" +IDB_ID_NEW_DIS_SM BITMAP "..\\..\\images\\id-new-dis-sm.bmp" +IDB_ID_NEW_SM BITMAP "..\\..\\images\\id-new-sm.bmp" +IDB_ID_REFRESH_DIS BITMAP "..\\..\\images\\id-refresh-dis.bmp" +IDB_ID_REFRESH_SM BITMAP "..\\..\\images\\id-refresh-sm.bmp" +IDB_ID_REFRESH_DIS_SM BITMAP "..\\..\\images\\id-refresh-sm-dis.bmp" +IDB_TK_DELETE_DIS BITMAP "..\\..\\images\\tk-delete-dis.bmp" +IDB_TK_DELETE_DIS_SM BITMAP "..\\..\\images\\tk-delete-dis-sm.bmp" +IDB_TK_DELETE_SM BITMAP "..\\..\\images\\tk-delete-sm.bmp" +IDB_TK_DIS_SM BITMAP "..\\..\\images\\tk-dis-sm.bmp" +IDB_TK_NEW_DIS BITMAP "..\\..\\images\\tk-new-dis.bmp" +IDB_TK_NEW_DIS_SM BITMAP "..\\..\\images\\tk-new-dis-sm.bmp" +IDB_TK_NEW_SM BITMAP "..\\..\\images\\tk-new-sm.bmp" +IDB_TK_REFRESH_DIS BITMAP "..\\..\\images\\tk-refresh-dis.bmp" +IDB_TK_REFRESH_DIS_SM BITMAP "..\\..\\images\\tk-refresh-dis-sm.bmp" +IDB_TK_REFRESH_SM BITMAP "..\\..\\images\\tk-refresh-sm.bmp" +IDB_TK_SM BITMAP "..\\..\\images\\tk-sm.bmp" +IDB_HELP_SM BITMAP "..\\..\\images\\help-sm.bmp" +IDB_HELP BITMAP "..\\..\\images\\help.bmp" +IDB_LOGO_SHADE BITMAP "..\\..\\images\\logo_shade.bmp" +IDB_WDG_EXPAND BITMAP "..\\..\\images\\wdg_expanded.bmp" +IDB_WDG_COLLAPSE BITMAP "..\\..\\images\\wdg_collapsed.bmp" +IDB_ID_SM BITMAP "..\\..\\images\\id-sm.bmp" +IDB_WDG_EXPAND_HI BITMAP "..\\..\\images\\wdg_expanded_hi.bmp" +IDB_WDG_COLLAPSE_HI BITMAP "..\\..\\images\\wdg_collapsed_hi.bmp" +IDB_WDG_CREDTYPE BITMAP "..\\..\\images\\wdg_credtype.bmp" +IDB_WDG_FLAG BITMAP "..\\..\\images\\wdg_flag.bmp" +IDB_FLAG_WARN BITMAP "..\\..\\images\\flag-warning.bmp" +IDB_FLAG_EXPIRED BITMAP "..\\..\\images\\flag_expired.bmp" +IDB_FLAG_CRITICAL BITMAP "..\\..\\images\\flag-critical.bmp" +IDB_LOGO_OPAQUE BITMAP "..\\..\\images\\khimaira-cfg.bmp" +IDB_IMPORT_SM_DIS BITMAP "..\\..\\images\\import-sm-dis.bmp" +IDB_IMPORT BITMAP "..\\..\\images\\import.bmp" +IDB_IMPORT_DIS BITMAP "..\\..\\images\\import-dis.bmp" +IDB_IMPORT_SM BITMAP "..\\..\\images\\import-sm.bmp" +IDB_CHPW_SM BITMAP "..\\..\\images\\chpw-sm.bmp" +IDB_CHPW BITMAP "..\\..\\images\\chpw.bmp" +IDB_CHPW_DIS BITMAP "..\\..\\images\\chpw-dis.bmp" +IDB_CHPW_DIS_SM BITMAP "..\\..\\images\\chpw-dis-sm.bmp" +IDB_TB_SPACE BITMAP "..\\..\\images\\tb-space.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_MENU_BAR ACCELERATORS +BEGIN + VK_F10, IDA_ACTIVATE_MENU, VIRTKEY, NOINVERT + VK_UP, IDA_UP, VIRTKEY, NOINVERT + VK_DOWN, IDA_DOWN, VIRTKEY, NOINVERT + VK_LEFT, IDA_LEFT, VIRTKEY, NOINVERT + VK_RIGHT, IDA_RIGHT, VIRTKEY, NOINVERT + VK_ESCAPE, IDA_ESC, VIRTKEY, NOINVERT + VK_EXECUTE, IDA_ENTER, VIRTKEY, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_NC_NEWCRED DIALOGEX 0, 0, 301, 167 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "UI Row2",IDC_NC_TPL_ROW_LG,7,31,287,18,NOT WS_VISIBLE | + WS_BORDER + LTEXT "TplPanel",IDC_NC_TPL_PANEL,7,7,287,153,NOT WS_VISIBLE | + WS_BORDER + LTEXT "UI Row",IDC_NC_TPL_ROW,7,7,287,18,NOT WS_VISIBLE | + WS_BORDER + LTEXT "TplLabel",IDC_NC_TPL_LABEL,7,8,45,10,NOT WS_VISIBLE | + WS_BORDER + LTEXT "TplInput",IDC_NC_TPL_INPUT,54,7,240,13,NOT WS_VISIBLE | + WS_BORDER + LTEXT "TplLabelLg",IDC_NC_TPL_LABEL_LG,7,33,146,10,NOT + WS_VISIBLE | WS_BORDER + LTEXT "TplInputLg",IDC_NC_TPL_INPUT_LG,155,31,139,13,NOT + WS_VISIBLE | WS_BORDER + LTEXT "&Credentials",IDC_NC_CREDTEXT_LABEL,7,66,41,10,NOT + WS_GROUP + CONTROL "",IDC_NC_CREDTEXT,"KhmHtWnd",WS_TABSTOP,54,65,240,73, + WS_EX_CLIENTEDGE + PUSHBUTTON "&Ok",IDOK,57,142,89,18,WS_DISABLED + PUSHBUTTON "&Cancel",IDCANCEL,158,142,54,18 + PUSHBUTTON "&Options >>",IDC_NC_OPTIONS,223,142,71,18 +END + +IDD_NC_BBAR DIALOGEX 0, 0, 60, 181 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "&Ok",IDOK,0,7,53,41,WS_DISABLED + PUSHBUTTON "&Cancel",IDCANCEL,0,58,53,19 + PUSHBUTTON "&Help",IDC_NC_HELP,0,155,53,19 +END + +IDD_NC_TS DIALOGEX 0, 0, 300, 15 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN +END + +IDD_PP_IDENT DIALOGEX 0, 0, 235, 156 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_CAPTION +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Identity" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Name",IDC_STATIC,7,8,19,12 + LTEXT "IdentityName",IDC_PP_IDNAME,34,7,194,12,NOT WS_GROUP, + WS_EX_CLIENTEDGE + CONTROL "Default identity",IDC_PP_IDDEF,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,34,25,71,12 + CONTROL "Searchable",IDC_PP_IDSEARCH,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,34,43,74,12 + CONTROL "Custom1",IDC_PP_PROPLIST,"NetIDMgrPropertyWnd", + WS_TABSTOP,7,61,221,88 + CONTROL "Always visible (sticky)",IDC_PP_STICKY,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,117,25,85,10 +END + +IDD_PP_CRED DIALOGEX 0, 0, 236, 158 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_CAPTION +CAPTION "Credential" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + CONTROL "Check1",IDC_PP_DUMMY,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_TABSTOP,0,1,39,10 + CONTROL "Custom1",IDC_PP_CPROPLIST,"NetIDMgrPropertyWnd", + WS_TABSTOP,7,7,222,144 +END + +IDD_CFG_MAIN DIALOGEX 0, 0, 357, 222 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +EXSTYLE WS_EX_CONTEXTHELP +CAPTION "Khimaira Configuration" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Title",IDC_CFG_TITLE,0,0,357,20,SS_CENTERIMAGE + CONTROL "",IDC_CFG_NODELIST,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | WS_TABSTOP,0,20,100,182 + LTEXT "Static",IDC_CFG_PANE,102,20,255,182,NOT WS_VISIBLE | + WS_BORDER + PUSHBUTTON "&Ok",IDOK,162,205,78,16 + PUSHBUTTON "&Cancel",IDCANCEL,246,205,51,16 + PUSHBUTTON "&Apply",IDAPPLY,303,205,51,16,WS_DISABLED + PUSHBUTTON "C&hange Summary ...",IDC_CFG_SUMMARY,3,205,76,16, + WS_DISABLED +END + +IDD_CFG_GENERIC DIALOGEX 0, 0, 255, 182 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CTEXT "Please select one of the configuration categories on the left.", + IDC_STATIC,21,17,212,18,SS_CENTERIMAGE,WS_EX_TRANSPARENT +END + +IDD_CFG_GENERAL DIALOGEX 0, 0, 255, 182 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Startup",IDC_CFG_STARTUP_GROUP,7,7,241,50 + CONTROL "&Prompt for new credentials if there aren't any at startup", + IDC_CFG_AUTOINIT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 16,22,196,10 + CONTROL "&Start NetIDMgr when Windows starts",IDC_CFG_AUTOSTART, + "Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,16, + 38,135,10 + GROUPBOX "Other",IDC_CFG_OTHER,7,63,241,70 + CONTROL "&Keep NetIDMgr running after closing window", + IDC_CFG_KEEPRUNNING,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,16,78,158,10 + CONTROL "Detect network connectivity",IDC_CFG_NETDETECT,"Button", + BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,16,96,106,10 + CONTROL "A&utomatically import credentials from Windows", + IDC_CFG_AUTOIMPORT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 16,113,165,10 +END + +IDD_CFG_IDENTITIES DIALOGEX 0, 0, 255, 182 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_CFG_TAB,"SysTabControl32",WS_TABSTOP,7,7,241,168 + LTEXT "Static",IDC_CFG_TARGET,10,21,235,151,NOT WS_VISIBLE | + WS_BORDER +END + +IDD_CFG_NOTIF DIALOGEX 0, 0, 255, 182 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "&Monitor credentials expiration",IDC_NOTIF_MONITOR, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,139,10 + CONTROL "&Renew automatically",IDC_NOTIF_RENEW,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,22,32,82,10 + EDITTEXT IDC_NOTIF_RENEW_THR,122,30,126,14,ES_AUTOHSCROLL + CONTROL "Warn",IDC_NOTIF_WARN1,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,22,57,33,10 + EDITTEXT IDC_NOTIF_WARN1_THR,122,55,126,14,ES_AUTOHSCROLL + CONTROL "Warn again",IDC_NOTIF_WARN2,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,22,82,67,10 + EDITTEXT IDC_NOTIF_WARN2_THR,122,80,126,14,ES_AUTOHSCROLL +END + +IDD_CFG_PLUGINS DIALOGEX 0, 0, 255, 182 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_CFG_PLUGINS,"SysListView32",LVS_ALIGNLEFT | + WS_BORDER | WS_TABSTOP,7,7,75,168 + GROUPBOX "Plugin",IDC_CFG_PLUGINGRP,86,7,162,103 + LTEXT "Description",IDC_CFG_LBL_DESC,90,20,36,8 + EDITTEXT IDC_CFG_DESC,134,17,109,14,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Status",IDC_CFG_LBL_STATE,90,38,22,8 + EDITTEXT IDC_CFG_STATE,134,35,109,14,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Depends on",IDC_CFG_LBL_DEPS,90,52,39,8 + LISTBOX IDC_CFG_DEPS,134,52,109,34,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Enable",IDC_CFG_ENABLE,134,90,50,14 + PUSHBUTTON "Disable",IDC_CFG_DISABLE,193,90,50,14 + GROUPBOX "Provided by",IDC_CFG_PROVGRP,86,111,162,47 + LTEXT "Module",IDC_CFG_LBL_MOD,90,124,24,8 + EDITTEXT IDC_CFG_MODULE,134,121,109,14,ES_AUTOHSCROLL | + ES_READONLY + LTEXT "Vendor",IDC_CFG_LBL_VEN,90,142,24,8 + EDITTEXT IDC_CFG_VENDOR,133,139,110,14,ES_AUTOHSCROLL | + ES_READONLY + PUSHBUTTON "Register new plugin ...",IDC_CFG_REGISTER,163,161,85,14, + WS_DISABLED +END + +IDD_CFG_IDENTITY DIALOGEX 0, 0, 255, 182 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_CFG_TAB,"SysTabControl32",WS_TABSTOP,7,7,241,168 + LTEXT "Static",IDC_CFG_TARGET,10,21,235,151,NOT WS_VISIBLE | + WS_BORDER +END + +IDD_CFG_IDS_TAB DIALOGEX 0, 0, 235, 151 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_CFG_IDENTS,"SysListView32",LVS_SHAREIMAGELISTS | + LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,221,72 + GROUPBOX "Selected identity",IDC_CFG_IDENTITY,7,81,221,63 + CONTROL "Monitor credential expiration",IDC_CFG_MONITOR,"Button", + BS_3STATE | WS_TABSTOP,13,92,107,10 + CONTROL "Automatically renew",IDC_CFG_RENEW,"Button",BS_3STATE | + WS_TABSTOP,13,106,81,10 + CONTROL "Always show in the credentials list (Sticky)", + IDC_CFG_STICKY,"Button",BS_3STATE | WS_TABSTOP,13,120, + 151,10 + PUSHBUTTON "&Remove",IDC_CFG_REMOVE,174,126,50,14,WS_DISABLED +END + +IDD_CFG_ID_TAB DIALOGEX 0, 0, 235, 151 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "Always show in the credentials list (Sticky)", + IDC_CFG_STICKY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7, + 34,151,10 + CONTROL "Monitor credential expiration",IDC_CFG_MONITOR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,7,107,10 + CONTROL "Automatically renew",IDC_CFG_RENEW,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,20,81,10 +END + +IDD_ABOUT DIALOGEX 0, 0, 268, 170 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "About Network Identity Manager" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,211,7,50,14 + LTEXT "Productname",IDC_PRODUCT,41,7,163,13,NOT WS_GROUP + LTEXT "© 2005 Massachusetts Institute of Technology", + IDC_COPYRIGHT,41,23,220,18,NOT WS_GROUP + LTEXT "BuildInfo",IDC_BUILDINFO,41,41,220,17,NOT WS_GROUP + ICON IDI_MAIN_APP,IDC_STATIC,6,7,21,20 + CONTROL "",IDC_MODULES,"SysListView32",LVS_ALIGNLEFT | WS_BORDER | + WS_TABSTOP,41,72,220,91 + LTEXT "Loaded modules",IDC_STATIC,41,60,52,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_NC_NEWCRED, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 294 + TOPMARGIN, 7 + BOTTOMMARGIN, 160 + END + + IDD_NC_BBAR, DIALOG + BEGIN + RIGHTMARGIN, 53 + TOPMARGIN, 7 + BOTTOMMARGIN, 174 + END + + IDD_PP_IDENT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 228 + VERTGUIDE, 34 + VERTGUIDE, 117 + TOPMARGIN, 7 + BOTTOMMARGIN, 149 + END + + IDD_PP_CRED, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 229 + TOPMARGIN, 7 + BOTTOMMARGIN, 151 + END + + IDD_CFG_GENERIC, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 248 + TOPMARGIN, 7 + BOTTOMMARGIN, 175 + END + + IDD_CFG_GENERAL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 248 + VERTGUIDE, 16 + TOPMARGIN, 7 + BOTTOMMARGIN, 175 + END + + IDD_CFG_IDENTITIES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 248 + VERTGUIDE, 10 + VERTGUIDE, 244 + TOPMARGIN, 7 + BOTTOMMARGIN, 175 + HORZGUIDE, 22 + HORZGUIDE, 171 + END + + IDD_CFG_NOTIF, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 248 + VERTGUIDE, 22 + VERTGUIDE, 122 + TOPMARGIN, 7 + BOTTOMMARGIN, 175 + END + + IDD_CFG_PLUGINS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 248 + VERTGUIDE, 86 + VERTGUIDE, 90 + VERTGUIDE, 134 + VERTGUIDE, 243 + TOPMARGIN, 7 + BOTTOMMARGIN, 175 + END + + IDD_CFG_IDENTITY, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 248 + TOPMARGIN, 7 + BOTTOMMARGIN, 175 + END + + IDD_CFG_IDS_TAB, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 228 + TOPMARGIN, 7 + BOTTOMMARGIN, 144 + END + + IDD_CFG_ID_TAB, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 228 + TOPMARGIN, 7 + BOTTOMMARGIN, 144 + END + + IDD_ABOUT, DIALOG + BEGIN + LEFTMARGIN, 6 + RIGHTMARGIN, 261 + VERTGUIDE, 41 + VERTGUIDE, 204 + TOPMARGIN, 7 + BOTTOMMARGIN, 163 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_MAIN_WINDOW_TITLE "Network Identity Manager" + IDS_MENU_FILE "&File" + IDS_MENU_CRED "&Credential" + IDS_MENU_VIEW "&View" +END + +STRINGTABLE +BEGIN + IDS_MENU_OPTIONS "&Options" + IDS_MENU_HELP "&Help" + IDS_ACTION_PROPERTIES "&Properties ..." + IDS_ACTION_EXIT "E&xit" + IDS_CFG_ROOT_NAME "NetIDMgr" + IDS_ACTION_SET_DEF_ID "Set as &default" + IDS_ACTION_SET_SRCH_ID "Allow applications to &search" + IDS_CFG_ROOT_TITLE "NetIDMgr Configuration" + IDS_CFG_GENERAL_SHORT "General" + IDS_ACTION_NEW_CRED "&New credentials ..." + IDS_ACTION_PASSWD_ID "Change &password ..." + IDS_ACTION_CHOOSE_COLS "Choose columns ..." + IDS_ACTION_DEBUG_WINDOW "Debug window ..." + IDS_ACTION_VIEW_REFRESH "Refresh" + IDS_MENU_LAYOUT "Layout" + IDS_MENU_TOOLBARS "Toolbars" +END + +STRINGTABLE +BEGIN + IDS_ACTION_LAYOUT_ID "By identity" + IDS_ACTION_LAYOUT_TYPE "By type" + IDS_ACTION_LAYOUT_LOC "By location" + IDS_ACTION_TB_STANDARD "Standard" + IDS_ACTION_OPT_KHIM "General ..." + IDS_ACTION_OPT_IDENTS "Identities ..." + IDS_ACTION_OPT_NOTIF "Notifications ..." + IDS_ACTION_HELP_CTX "Context" + IDS_ACTION_HELP_CONTENTS "Contents ..." + IDS_ACTION_HELP_INDEX "Index ..." + IDS_ACTION_HELP_ABOUT "About NetIDMgr ..." + IDS_CFG_GENERAL_LONG "General options for NetIDMgr" + IDS_SAMPLE_STRING "Wxy" + IDS_NO_CREDS "<large><center>You currently have no credentials.Click <a id=""NewCreds"">here</a> to obtain new credentials.</center></large>" + IDS_WT_INIT_CREDS "Obtain initial credentials" + IDS_WT_NEW_CREDS "Obtain new credentials" +END + +STRINGTABLE +BEGIN + IDS_NC_IDENTITY "&Identity" + IDS_NC_IDENTS "&Identities" + IDS_NC_CREDTEXT_ID_NONE "<p><b>(No identities specified)</b></p>" + IDS_NC_CREDTEXT_ID_ONE "<p>Selected identity: <b>%s</b></p>" + IDS_NC_CREDTEXT_ID_MANY "<p>Primary identity: <b>%s</b></p><p>Additional identities: <b>%s</b></p>" + IDS_NC_CREDTEXT_ID_INVALID "<font color=""red"">%s (invalid)</font>" + IDS_WTPOST_INIT_CREDS " - Initial credentials" + IDS_WTPOST_NEW_CREDS " - New credentials" + IDS_ACTION_RENEW_CRED "R&enew credentials" + IDS_ACTION_DESTROY_CRED "De&stroy credentials ..." + IDS_DEFAULT_FONT "MS Shell Dlg" + IDS_NC_CREDTEXT_TABS "<settab pos=""15""><settab pos=""30""><settab pos=""45"">" + IDS_NOTIFY_PREFIX "NetIDMgr - " + IDS_NOTIFY_READY "Ready" + IDS_NOTIFY_ATTENTION "Attention" + IDS_ALERT_DEFAULT "Alert" +END + +STRINGTABLE +BEGIN + IDS_PACTION_OK "&Ok" + IDS_PACTION_CANCEL "&Cancel" + IDS_PACTION_CLOSE "&Close" + IDS_ALERT_NOSEL_TITLE "No credentials selected" + IDS_ALERT_NOSEL "Please select a credential, a credential type or an identity." + IDS_NC_CREDTEXT_ID_VALID "<font color=""blue"">%s</font>" + IDS_NC_CREDTEXT_ID_UNCHECKED "<font color=""grey"">%s (Unverified)</font>" + IDS_PROP_COL_PROPERTY "Property" + IDS_PROP_COL_VALUE "Value" + IDS_NC_NEW_IDENT "( New identity ... )" + IDS_NC_CREDTEXT_ID_CHECKING "<font color=""grey"">%s (Checking...)</font>" + IDS_ACTION_OPEN_APP "Open NetIDMgr ..." + IDS_CTX_NEW_IDENT "Obaining new identity" + IDS_CTX_NEW_CREDS "Obtaining new credentials" + IDS_CTX_RENEW_CREDS "Renewing credentials" + IDS_CTX_PROC_NEW_IDENT "Obtaining initial credentials for %1!s!" +END + +STRINGTABLE +BEGIN + IDS_CTX_PROC_NEW_CREDS "Obtaining new credentials for %1!s!" + IDS_CTX_PROC_RENEW_CREDS "Renewing credentials for %1!s!" + IDS_ACTION_CLOSE_APP "Close NetIDMgr window" + IDS_NC_FAILED_TITLE "Failed to acquire credentials" + IDS_CFG_IDENTITIES_SHORT "Identities" + IDS_CFG_IDENTITIES_LONG "Options for all identities" + IDS_CFG_NOTIF_SHORT "Notifications" + IDS_CFG_NOTIF_LONG "Notifications" + IDS_CFG_PLUGINS_SHORT "Plugins" + IDS_CFG_PLUGINS_LONG "Plugins and Modules" + IDS_CFG_IDENTITY_SHORT "%s" + IDS_CFG_IDENTITY_LONG "Options for %s" + IDS_CTX_DESTROY_CREDS "Destroying credentials" + IDS_WARN_EXPIRE "Some of your credentials will expire in %s" + IDS_WARN_TITLE "Credentials expiration warning" + IDS_ALERT_MOREINFO "...\nClick here for more..." +END + +STRINGTABLE +BEGIN + IDS_WARN_EXPIRED "Some of your credentials have expired." + IDS_WARN_EXPIRE_ID "Credentials for %.180s will expire in %s" + IDS_WARN_EXPIRED_ID "Credentials for %.220s have expired" + IDS_WARN_WM_TITLE "NetIDMgr is still running" + IDS_WARN_WM_MSG "Click the NetIDMgr icon below to open the application.\n\nOr right click the icon to access the NetIDMgr menu." + IDS_CFG_ID_TAB_SHORT "General" + IDS_CFG_ID_TAB_LONG "General options for this identity" + IDS_CFG_IDS_TAB_SHORT "General" + IDS_CFG_IDS_TAB_LONG "General options for all identities" + IDS_CFG_IDS_IDENTITY "Identity" + IDS_ACTION_IMPORT "Import Credentials" + IDS_CTX_IMPORT "Importing credentials from Windows" + IDS_CFG_PI_COL_PLUGINS "Plugins" + IDS_PISTATE_FAILUNK "Unknown failure" + IDS_PISTATE_FAILMAX "Maximum failure count reached" + IDS_PISTATE_FAILREG "Not properly registered" +END + +STRINGTABLE +BEGIN + IDS_PISTATE_FAILDIS "Disabled" + IDS_PISTATE_FAILLOD "Failed to initialize" + IDS_PISTATE_PLACEHOLD "Not loaded" + IDS_PISTATE_REG "Not initialized" + IDS_PISTATE_HOLD "Waiting for dependencies" + IDS_PISTATE_INIT "Initializing" + IDS_PISTATE_RUN "Running" + IDS_PISTATE_EXIT "Stopped" + IDS_CTX_PASSWORD "Changing password" + IDS_WT_PASSWORD "Changing password" + IDS_WTPOST_PASSWORD " - Changing password" + IDS_CTX_PROC_PASSWORD "Changing password for %1!s!" + IDS_NC_PWD_FAILED_TITLE "Failed to change password" + IDS_CMDLINE_HELP "Command line options for NetIDMgr are :\n\n-a or --autoinit: Auto initialize credentials\n-i or --kinit: Obtain new credentials\n-d or --destroy: Destroy default identity\n-r or --renew: Renew all credentials" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/windows/identity/ui/main.c b/src/windows/identity/ui/main.c new file mode 100644 index 000000000..b92c540b5 --- /dev/null +++ b/src/windows/identity/ui/main.c @@ -0,0 +1,442 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khmapp.h> + +#if DEBUG +#include<assert.h> +#endif + +HINSTANCE khm_hInstance; +const wchar_t * khm_facility = L"NetIDMgr"; +int khm_nCmdShow; + +khm_startup_options khm_startup; + +void khm_init_gui(void) { + khui_init_actions(); + khui_init_rescache(); + khui_init_menu(); + khui_init_toolbar(); + khui_init_notifier(); + khm_init_config(); +} + +void khm_exit_gui(void) { + khm_exit_config(); + khui_exit_notifier(); + khui_exit_toolbar(); + khui_exit_menu(); + khui_exit_rescache(); + khui_exit_actions(); +} + +void khm_parse_commandline(void) { + LPWSTR wcmdline; + LPWSTR * wargs; + int wargc; + int i; + + ZeroMemory(&khm_startup, sizeof(khm_startup)); + + wcmdline = GetCommandLine(); + wargs = CommandLineToArgvW(wcmdline, &wargc); + + for (i=1; i<wargc; i++) { + if (!wcscmp(wargs[i], L"-i") || + !wcscmp(wargs[i], L"--kinit")) { + khm_startup.init = TRUE; + khm_startup.exit = TRUE; + khm_startup.no_main_window = TRUE; + } + else if (!wcscmp(wargs[i], L"-m") || + !wcscmp(wargs[i], L"--import")) { + khm_startup.import = TRUE; + khm_startup.exit = TRUE; + khm_startup.no_main_window = TRUE; + } + else if (!wcscmp(wargs[i], L"-r") || + !wcscmp(wargs[i], L"--renew")) { + khm_startup.renew = TRUE; + khm_startup.exit = TRUE; + khm_startup.no_main_window = TRUE; + } + else if (!wcscmp(wargs[i], L"-d") || + !wcscmp(wargs[i], L"--destroy")) { + khm_startup.destroy = TRUE; + khm_startup.exit = TRUE; + khm_startup.no_main_window = TRUE; + } + else if (!wcscmp(wargs[i], L"-a") || + !wcscmp(wargs[i], L"--autoinit")) { + khm_startup.autoinit = TRUE; + } + else { + wchar_t help[2048]; + + LoadString(khm_hInstance, IDS_CMDLINE_HELP, + help, ARRAYLENGTH(help)); + + MessageBox(NULL, help, L"NetIDMgr", MB_OK); + + khm_startup.error_exit = TRUE; + break; + } + } +} + +void khm_register_window_classes(void) { + INITCOMMONCONTROLSEX ics; + + ZeroMemory(&ics, sizeof(ics)); + ics.dwSize = sizeof(ics); + ics.dwICC = + ICC_COOL_CLASSES | + ICC_BAR_CLASSES | + ICC_DATE_CLASSES | + ICC_HOTKEY_CLASS | + ICC_LINK_CLASS | + ICC_LISTVIEW_CLASSES | + ICC_STANDARD_CLASSES | + ICC_TAB_CLASSES; + InitCommonControlsEx(&ics); + + khm_register_main_wnd_class(); + khm_register_credwnd_class(); + khm_register_htwnd_class(); + khm_register_passwnd_class(); + khm_register_newcredwnd_class(); + khm_register_propertywnd_class(); +} + +void khm_unregister_window_classes(void) { + khm_unregister_main_wnd_class(); + khm_unregister_credwnd_class(); + khm_unregister_htwnd_class(); + khm_unregister_passwnd_class(); + khm_unregister_newcredwnd_class(); + khm_unregister_propertywnd_class(); +} + + +/* we support up to 16 simutaneous dialogs. In reality, more than two + is pretty unlikely. Property sheets are special and are handled + separately. */ +#define MAX_UI_DIALOGS 16 + +typedef struct tag_khui_dialog { + HWND hwnd; + BOOL active; +} khui_dialog; + +static khui_dialog khui_dialogs[MAX_UI_DIALOGS]; +static int n_khui_dialogs = 0; +static HWND khui_modal_dialog = NULL; +static BOOL khui_main_window_active; + +/* should only be called from the UI thread */ +void khm_add_dialog(HWND dlg) { + if(n_khui_dialogs < MAX_UI_DIALOGS - 1) { + khui_dialogs[n_khui_dialogs].hwnd = dlg; + /* we set .active=FALSE for now. We don't need this to have a + meaningful value until we enter a modal loop */ + khui_dialogs[n_khui_dialogs].active = FALSE; + n_khui_dialogs++; + } +#if DEBUG + else { + assert(FALSE); + } +#endif +} + +/* should only be called from the UI thread */ +void khm_enter_modal(HWND hwnd) { + int i; + + for(i=0; i < n_khui_dialogs; i++) { + if(khui_dialogs[i].hwnd != hwnd) { + khui_dialogs[i].active = IsWindowEnabled(khui_dialogs[i].hwnd); + EnableWindow(khui_dialogs[i].hwnd, FALSE); + } + } + + khui_main_window_active = IsWindowEnabled(khm_hwnd_main); + EnableWindow(khm_hwnd_main, FALSE); + + khui_modal_dialog = hwnd; +} + +/* should only be called from the UI thread */ +void khm_leave_modal(void) { + int i; + + for(i=0; i < n_khui_dialogs; i++) { + if(khui_dialogs[i].hwnd != khui_modal_dialog) { + EnableWindow(khui_dialogs[i].hwnd, khui_dialogs[i].active); + } + } + + EnableWindow(khm_hwnd_main, khui_main_window_active); + + khui_modal_dialog = NULL; +} + +/* should only be called from the UI thread */ +void khm_del_dialog(HWND dlg) { + int i; + for(i=0;i < n_khui_dialogs; i++) { + if(khui_dialogs[i].hwnd == dlg) + break; + } + + if(i < n_khui_dialogs) + n_khui_dialogs--; + else + return; + + for(;i < n_khui_dialogs; i++) { + khui_dialogs[i] = khui_dialogs[i+1]; + } +} + +BOOL khm_check_dlg_message(LPMSG pmsg) { + int i; + for(i=0;i<n_khui_dialogs;i++) { + if(IsDialogMessage(khui_dialogs[i].hwnd, pmsg)) + break; + } + + if(i<n_khui_dialogs) + return TRUE; + else + return FALSE; +} + +BOOL khm_is_dialog_active(void) { + HWND hwnd; + int i; + + hwnd = GetForegroundWindow(); + + for (i=0; i<n_khui_dialogs; i++) { + if (khui_dialogs[i].hwnd == hwnd) + return TRUE; + } + + return FALSE; +} + +/* We support at most 256 property sheets simultaneously. 256 + property sheets should be enough for everybody. */ +#define MAX_UI_PROPSHEETS 256 + +khui_property_sheet *_ui_propsheets[MAX_UI_PROPSHEETS]; +int _n_ui_propsheets = 0; + +void khm_add_property_sheet(khui_property_sheet * s) { + if(_n_ui_propsheets < MAX_UI_PROPSHEETS) + _ui_propsheets[_n_ui_propsheets++] = s; +#ifdef DEBUG + else + assert(FALSE); +#endif +} + +void khm_del_property_sheet(khui_property_sheet * s) { + int i; + + for(i=0;i < _n_ui_propsheets; i++) { + if(_ui_propsheets[i] == s) + break; + } + + if(i < _n_ui_propsheets) + _n_ui_propsheets--; + else + return; + + for(;i < _n_ui_propsheets; i++) { + _ui_propsheets[i] = _ui_propsheets[i+1]; + } +} + +BOOL khm_check_ps_message(LPMSG pmsg) { + int i; + khui_property_sheet * ps; + for(i=0;i<_n_ui_propsheets;i++) { + if(khui_ps_check_message(_ui_propsheets[i], pmsg)) { + if(_ui_propsheets[i]->status == KHUI_PS_STATUS_DONE) { + ps = _ui_propsheets[i]; + + ps->status = KHUI_PS_STATUS_DESTROY; + kmq_post_message(KMSG_CRED, KMSG_CRED_PP_END, 0, (void *) ps); + + return TRUE; + } + return TRUE; + } + } + + return FALSE; +} + +WPARAM khm_message_loop(void) { + int r; + MSG msg; + HACCEL ha_menu; + + ha_menu = khui_create_global_accel_table(); + while(r = GetMessage(&msg, NULL, 0,0)) { + if(r == -1) + break; + if(!khm_check_dlg_message(&msg) && + !khm_check_ps_message(&msg) && + !TranslateAccelerator(khm_hwnd_main, ha_menu, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + DestroyAcceleratorTable(ha_menu); + return msg.wParam; +} + +int WINAPI WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow) +{ + int rv = 0; + HANDLE h_appmutex; + BOOL slave = FALSE; + + khm_hInstance = hInstance; + khm_nCmdShow = nCmdShow; + + khm_parse_commandline(); + + if (khm_startup.error_exit) + return 0; + + h_appmutex = CreateMutex(NULL, FALSE, L"Local\\NetIDMgr_GlobalAppMutex"); + if (h_appmutex == NULL) + return 5; + if (GetLastError() == ERROR_ALREADY_EXISTS) + slave = TRUE; + + khc_load_schema(NULL, schema_uiconfig); + + if(!slave) { + /* we only open a main window if this is the only instance + of the application that is running. */ + kmq_init(); + kmm_init(); + khm_init_gui(); + + kmq_set_completion_handler(KMSG_CRED, kmsg_cred_completion); + + /* load the standard plugins */ + kmm_load_default_modules(); + + khm_register_window_classes(); + + khm_init_request_daemon(); + + khm_create_main_window(); + + if (!khm_startup.no_main_window) + khm_show_main_window(); + + rv = (int) khm_message_loop(); + + kmq_set_completion_handler(KMSG_CRED, NULL); + + khm_exit_request_daemon(); + + khm_exit_gui(); + khm_unregister_window_classes(); + kmm_exit(); + kmq_exit(); + + CloseHandle(h_appmutex); + } else { + HWND hwnd = NULL; + int retries = 5; + HANDLE hmap; + wchar_t mapname[256]; + DWORD tid; + void * xfer; + + CloseHandle(h_appmutex); + + while (hwnd == NULL && retries) { + hwnd = FindWindowEx(NULL, NULL, KHUI_MAIN_WINDOW_CLASS, NULL); + + if (hwnd) + break; + + retries--; + Sleep(1000); + } + + if (!hwnd) + return 2; + + StringCbPrintf(mapname, sizeof(mapname), + COMMANDLINE_MAP_FMT, + (tid = GetCurrentThreadId())); + + hmap = CreateFileMapping(INVALID_HANDLE_VALUE, + NULL, + PAGE_READWRITE, + 0, + 4096, + mapname); + + if (hmap == NULL) + return 3; + + xfer = MapViewOfFile(hmap, + FILE_MAP_WRITE, + 0, 0, + sizeof(khm_startup)); + + if (xfer) { + memcpy(xfer, &khm_startup, sizeof(khm_startup)); + + SendMessage(hwnd, WM_KHUI_ASSIGN_COMMANDLINE, + 0, (LPARAM) tid); + } + + if (xfer) + UnmapViewOfFile(xfer); + + if (hmap) + CloseHandle(hmap); + } + + return rv; +} diff --git a/src/windows/identity/ui/mainmenu.c b/src/windows/identity/ui/mainmenu.c new file mode 100644 index 000000000..6115204f3 --- /dev/null +++ b/src/windows/identity/ui/mainmenu.c @@ -0,0 +1,566 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khmapp.h> +#include<assert.h> + +HWND khui_main_menu_toolbar; +int mm_last_hot_item = -1; +int mm_next_hot_item = -1; +BOOL mm_hot_track = FALSE; + +#define MAX_ILIST 256 +/* not the same as MENU_SIZE_ICON_* */ +#define ILIST_ICON_X 16 +#define ILIST_ICON_Y 15 + +khui_ilist * il_icon; +int il_icon_id[MAX_ILIST]; + +void khui_init_menu(void) { + int i; + + il_icon = khui_create_ilist(ILIST_ICON_X, + ILIST_ICON_Y, + MAX_ILIST, 5, 0); + for(i=0;i<MAX_ILIST;i++) + il_icon_id[i] = -1; +} + +void khui_exit_menu(void) { + khui_delete_ilist(il_icon); +} + +int khui_get_icon_index(int id) { + int i; + HBITMAP hbm; + + for(i=0;i<MAX_ILIST;i++) + if(il_icon_id[i] == id) { + return i; + } + + hbm = LoadImage(khm_hInstance, + MAKEINTRESOURCE(id), + IMAGE_BITMAP, + ILIST_ICON_X, ILIST_ICON_Y, + LR_DEFAULTCOLOR); + i = khui_ilist_add_masked(il_icon, hbm, KHUI_TOOLBAR_BGCOLOR); + il_icon_id[i] = id; + DeleteObject(hbm); + + return i; +} + +void add_action_to_menu(HMENU hm, khui_action * act, + int idx, int flags) { + MENUITEMINFO mii; + wchar_t buf[MAX_RES_STRING] = L""; + wchar_t accel[MAX_RES_STRING] = L""; + + mii.cbSize = sizeof(mii); + mii.fMask = 0; + + if(act == NULL) { + mii.fMask = MIIM_FTYPE; + mii.fType = MFT_SEPARATOR; + } else { + khui_menu_def * def; + + LoadString(khm_hInstance, + act->is_caption, + buf, ARRAYLENGTH(buf)); + + if(khui_get_cmd_accel_string(act->cmd, accel, + ARRAYLENGTH(accel))) { + StringCbCat(buf, sizeof(buf), L"\t"); + StringCbCat(buf, sizeof(buf), accel); + } + + mii.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID; + mii.fType = MFT_STRING; + + mii.dwTypeData = buf; + mii.cch = (int) wcslen(buf); + + mii.wID = act->cmd; + + if(act->state & KHUI_ACTIONSTATE_DISABLED) { + mii.fMask |= MIIM_STATE; + mii.fState = MFS_DISABLED; + } else { + mii.fState = 0; + } + + if((act->type & KHUI_ACTIONTYPE_TOGGLE) && + (act->state & KHUI_ACTIONSTATE_CHECKED)) { + mii.fMask |= MIIM_STATE; + mii.fState |= MFS_CHECKED; + } + + if(act->ib_icon) { + mii.fMask |= MIIM_BITMAP; + mii.hbmpItem = HBMMENU_CALLBACK; + } + + def = khui_find_menu(act->cmd); + if(def) { + mii.fMask |= MIIM_SUBMENU; + mii.hSubMenu = mm_create_menu_from_def(def); + } + + if(flags & KHUI_ACTIONREF_DEFAULT) + mii.fState |= MFS_DEFAULT; + } + + InsertMenuItem(hm,idx,TRUE,&mii); +} + +static HMENU mm_create_menu_from_def(khui_menu_def * def) { + HMENU hm; + khui_action_ref * act; + int i; + + hm = CreatePopupMenu(); + act = def->items; + i = 0; + while(act->action != KHUI_MENU_END) { + add_action_to_menu(hm,khui_find_action(act->action),i,act->flags); + act++; i++; + } + + return hm; +} + +void mm_begin_hot_track(void); +void mm_end_hot_track(void); + +static void mm_show_panel_def(khui_menu_def * def, LONG x, LONG y) +{ + HMENU hm; + + hm = mm_create_menu_from_def(def); + + mm_hot_track = (mm_last_hot_item >= 0); + + if (mm_hot_track) + mm_begin_hot_track(); + + TrackPopupMenuEx(hm, + TPM_LEFTALIGN | TPM_TOPALIGN | + TPM_VERPOSANIMATION, + x, y, khm_hwnd_main, NULL); + + mm_last_hot_item = -1; + + if (mm_hot_track) + mm_end_hot_track(); + + mm_hot_track = FALSE; + + DestroyMenu(hm); +} + +void khm_menu_show_panel(int id, LONG x, LONG y) { + khui_menu_def * def; + + def = khui_find_menu(id); + if(!def) + return; + + mm_show_panel_def(def, x, y); +} + +LRESULT khm_menu_activate(int menu_id) { + khui_menu_def * mmdef; + int nmm; + + mmdef = khui_find_menu(KHUI_MENU_MAIN); + nmm = (int) khui_action_list_length(mmdef->items); + + if(menu_id == MENU_ACTIVATE_DEFAULT) { + if (mm_last_hot_item != -1) + menu_id = mm_last_hot_item; + else + menu_id = 0; + } else if(menu_id == MENU_ACTIVATE_LEFT) { + menu_id = (mm_last_hot_item > 0)? + mm_last_hot_item - 1: + ((mm_last_hot_item == 0)? nmm - 1: 0); + } else if(menu_id == MENU_ACTIVATE_RIGHT) { + menu_id = (mm_last_hot_item >=0 && mm_last_hot_item < nmm - 1)? + mm_last_hot_item + 1: + 0; + } else if(menu_id == MENU_ACTIVATE_NONE) { + menu_id = -1; + } + + + SendMessage(khui_main_menu_toolbar, + TB_SETHOTITEM, + menu_id, + 0); + + + khm_menu_track_current(); + + return TRUE; +} + +LRESULT khm_menu_measure_item(WPARAM wParam, LPARAM lParam) { + /* all menu icons have a fixed size */ + LPMEASUREITEMSTRUCT lpm = (LPMEASUREITEMSTRUCT) lParam; + lpm->itemWidth = MENU_SIZE_ICON_X; + lpm->itemHeight = MENU_SIZE_ICON_Y; + return TRUE; +} + +LRESULT khm_menu_draw_item(WPARAM wParam, LPARAM lParam) { + LPDRAWITEMSTRUCT lpd; + khui_action * act; + int resid; + int iidx; + UINT style; + + lpd = (LPDRAWITEMSTRUCT) lParam; + act = khui_find_action(lpd->itemID); + + resid = 0; + if((lpd->itemState & ODS_DISABLED) || (lpd->itemState & ODS_GRAYED)) { + resid = act->ib_icon_dis; + } + if(!resid) + resid = act->ib_icon; + + if(!resid) /* nothing to draw */ + return TRUE; + + + iidx = khui_get_icon_index(resid); + if(iidx == -1) + return TRUE; + + + style = ILD_TRANSPARENT; + if(lpd->itemState & ODS_HOTLIGHT || lpd->itemState & ODS_SELECTED) { + style |= ILD_SELECTED; + } + + khui_ilist_draw(il_icon, + iidx, + lpd->hDC, + lpd->rcItem.left, lpd->rcItem.top, style); + + return TRUE; +} + +void khm_track_menu(int menu) { + TBBUTTON bi; + RECT r; + RECT wr; + + if (menu != -1) + mm_last_hot_item = menu; + + if (mm_last_hot_item != -1) { + SendMessage(khui_main_menu_toolbar, + TB_GETBUTTON, + mm_last_hot_item, + (LPARAM) &bi); + + SendMessage(khui_main_menu_toolbar, + TB_GETITEMRECT, + mm_last_hot_item, + (LPARAM) &r); + + GetWindowRect(khui_main_menu_toolbar, &wr); + + khm_menu_show_panel(bi.idCommand, wr.left + r.left, wr.top + r.bottom); + + r.left = 0; + + if (mm_next_hot_item != -1) { + mm_last_hot_item = mm_next_hot_item; + mm_next_hot_item = -1; + + PostMessage(khm_hwnd_main, WM_COMMAND, + MAKEWPARAM(KHUI_PACTION_MENU,0), + MAKELPARAM(mm_last_hot_item,1)); + } + } +} + +void khm_menu_track_current(void) { + khm_track_menu(-1); +} + +LRESULT khm_menu_handle_select(WPARAM wParam, LPARAM lParam) { + if((HIWORD(wParam) == 0xffff && lParam == 0) || + (HIWORD(wParam) & MF_POPUP)) { + /* the menu was closed */ + khui_statusbar_set_text(KHUI_SBPART_INFO, NULL); + } else { + khui_action * act; + int id; + wchar_t buf[MAX_RES_STRING] = L""; + + id = LOWORD(wParam); + act = khui_find_action(id); + if(act == NULL || act->is_tooltip == 0) + khui_statusbar_set_text(KHUI_SBPART_INFO, NULL); + else { + LoadString(khm_hInstance, + act->is_tooltip, + buf, ARRAYLENGTH(buf)); + khui_statusbar_set_text(KHUI_SBPART_INFO, buf); + } + } + return 0; +} + +HHOOK mm_hevt_hook = NULL; +HWND mm_hwnd_menu_panel = NULL; + +LRESULT CALLBACK mm_event_filter(int code, + WPARAM wParam, + LPARAM lParam) { + MSG * m; + RECT r; + int x,y; + + if (code == MSGF_MENU) { + /* do stuff */ + m = (MSG *) lParam; + GetWindowRect(khui_main_menu_toolbar, &r); + + if (m->hwnd != khm_hwnd_main) + mm_hwnd_menu_panel = m->hwnd; + + switch(m->message) { + case WM_MOUSEMOVE: + + x = GET_X_LPARAM(m->lParam); + y = GET_Y_LPARAM(m->lParam); + x -= r.left; + y -= r.top; + + SendMessage(khui_main_menu_toolbar, + m->message, + m->wParam, + MAKELPARAM(x,y)); + break; + } + } + + return CallNextHookEx(mm_hevt_hook, code, wParam, lParam); +} + + +void mm_begin_hot_track(void) { + + if (mm_hevt_hook) + UnhookWindowsHookEx(mm_hevt_hook); + + mm_hevt_hook = SetWindowsHookEx(WH_MSGFILTER, + mm_event_filter, + NULL, + GetCurrentThreadId()); +} + +void mm_end_hot_track(void) { + if (mm_hevt_hook) + UnhookWindowsHookEx(mm_hevt_hook); + + mm_hevt_hook = NULL; + mm_hwnd_menu_panel = NULL; +} + +void mm_cancel_menu(void) { + if (mm_hwnd_menu_panel) + SendMessage(mm_hwnd_menu_panel, WM_CANCELMODE, 0, 0); +} + +LRESULT khm_menu_notify_main(LPNMHDR notice) { + LPNMTOOLBAR nmt; + LRESULT ret = FALSE; + RECT r; + khui_menu_def * mmdef; + khui_action_ref * mm; + int nmm; + + mmdef = khui_find_menu(KHUI_MENU_MAIN); + mm = mmdef->items; + nmm = (int) khui_action_list_length(mm); + + GetWindowRect(khui_main_menu_toolbar, &r); + + nmt = (LPNMTOOLBAR) notice; + switch(notice->code) { + case TBN_DROPDOWN: + khm_track_menu(-1); + /* + khm_menu_show_panel(nmt->iItem, + r.left + nmt->rcButton.left, + r.top + nmt->rcButton.bottom); + */ + ret = TBDDRET_DEFAULT; + break; + + case TBN_HOTITEMCHANGE: + { + LPNMTBHOTITEM nmhi; + int new_item = -1; + + nmhi = (LPNMTBHOTITEM) notice; + + if(nmhi->dwFlags & HICF_LEAVING) + new_item = -1; + else { + int i; + for(i=0; i < nmm; i++) { + if(mm[i].action == nmhi->idNew) { + new_item = i; + break; + } + } + } + + if (mm_hot_track && + new_item != mm_last_hot_item && + new_item != -1 && + mm_last_hot_item != -1) { + + EndMenu(); + mm_next_hot_item = new_item; + + } + + ret = 0; + + if (!mm_hot_track || new_item != -1) + mm_last_hot_item = new_item; + + } break; + + default: + /* hmm. what to do */ + ret = FALSE; + } + return ret; +} + +void khm_menu_create_main(HWND rebar) { + HWND hwtb; + REBARBANDINFO rbi; + SIZE sz; + int i; + khui_menu_def * mmdef; + khui_action_ref * mm; + int nmm; + + mmdef = khui_find_menu(KHUI_MENU_MAIN); + mm = mmdef->items; + nmm = (int) khui_action_list_length(mm); + + hwtb = CreateWindowEx( + TBSTYLE_EX_MIXEDBUTTONS, + TOOLBARCLASSNAME, + (LPWSTR) NULL, + WS_CHILD | + CCS_ADJUSTABLE | + TBSTYLE_FLAT | + TBSTYLE_AUTOSIZE | + TBSTYLE_LIST | + CCS_NORESIZE | + CCS_NOPARENTALIGN | + CCS_NODIVIDER, + 0, 0, 0, 0, rebar, + (HMENU) NULL, khm_hInstance, + NULL); + + if(!hwtb) { +#ifdef DEBUG + assert(FALSE); +#else + return; +#endif + } + + khui_main_menu_toolbar = hwtb; + + SendMessage(hwtb, + TB_BUTTONSTRUCTSIZE, + (WPARAM) sizeof(TBBUTTON), + 0); + + for(i=0; i<nmm; i++) { + khui_add_action_to_toolbar(hwtb, + khui_find_action(mm[i].action), + KHUI_TOOLBAR_ADD_TEXT | + KHUI_TOOLBAR_ADD_DROPDOWN | + KHUI_TOOLBAR_VARSIZE, + NULL); + } + + SendMessage(hwtb, + TB_AUTOSIZE, + 0,0); + + SendMessage(hwtb, + TB_GETMAXSIZE, + 0, + (LPARAM) &sz); + + ZeroMemory(&rbi, sizeof(rbi)); + + rbi.cbSize = sizeof(rbi); + + rbi.fMask = + RBBIM_ID | + RBBIM_STYLE | + RBBIM_CHILD | + RBBIM_CHILDSIZE | + RBBIM_SIZE | + RBBIM_IDEALSIZE; + + rbi.fStyle = + RBBS_USECHEVRON; + + rbi.hwndChild = hwtb; + rbi.wID = KHUI_MENU_MAIN; + rbi.cx = sz.cx; + rbi.cxMinChild = rbi.cx; + rbi.cxIdeal = rbi.cx; + rbi.cyMinChild = sz.cy; + rbi.cyChild = rbi.cyMinChild; + rbi.cyIntegral = rbi.cyMinChild; + rbi.cyMaxChild = rbi.cyMinChild; + + SendMessage(rebar, + RB_INSERTBAND, + 0, + (LPARAM) &rbi); +} diff --git a/src/windows/identity/ui/mainmenu.h b/src/windows/identity/ui/mainmenu.h new file mode 100644 index 000000000..7cd8e01ca --- /dev/null +++ b/src/windows/identity/ui/mainmenu.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_MAINMENU_H +#define __KHIMAIRA_MAINMENU_H + +extern HWND khui_main_menu_toolbar; + +#define MENU_ACTIVATE_DEFAULT -1 +#define MENU_ACTIVATE_LEFT -2 +#define MENU_ACTIVATE_RIGHT -3 +#define MENU_ACTIVATE_NONE -4 + +extern int mm_last_hot_item; +extern BOOL mm_hot_track; + +void khm_menu_create_main(HWND rebar); +LRESULT khm_menu_handle_select(WPARAM wParam, LPARAM lParam); +LRESULT khm_menu_notify_main(LPNMHDR notice); +LRESULT khm_menu_activate(int menu_id); +void khm_menu_show_panel(int id, LONG x, LONG y); +void khm_menu_track_current(void); +LRESULT khm_menu_measure_item(WPARAM wParam, LPARAM lparam); +LRESULT khm_menu_draw_item(WPARAM wParam, LPARAM lparam); + +static HMENU mm_create_menu_from_def(khui_menu_def * def); +static void mm_show_panel_def(khui_menu_def * def, LONG x, LONG y); + +void khui_init_menu(void); +void khui_exit_menu(void); + +#define MENU_SIZE_ICON_X 16 +#define MENU_SIZE_ICON_Y 16 + +#endif diff --git a/src/windows/identity/ui/mainwnd.c b/src/windows/identity/ui/mainwnd.c new file mode 100644 index 000000000..0f5c7e043 --- /dev/null +++ b/src/windows/identity/ui/mainwnd.c @@ -0,0 +1,679 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khmapp.h> +#include<assert.h> + +ATOM khm_main_window_class; +ATOM khm_null_window_class; +HWND khm_hwnd_null; +HWND khm_hwnd_main; +HWND khm_hwnd_rebar; +HWND khm_hwnd_main_cred; + +#define MW_RESIZE_TIMER 1 +#define MW_RESIZE_TIMEOUT 2000 +#define MW_REFRESH_TIMER 2 +#define MW_REFRESH_TIMEOUT 600 + +void +khm_set_dialog_result(HWND hwnd, LRESULT lr) { +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, DWL_MSGRESULT, lr); +#pragma warning(pop) +} + +static void +mw_restart_refresh_timer(HWND hwnd) { + khm_handle csp_cw; + khm_int32 timeout; + + KillTimer(hwnd, MW_REFRESH_TIMER); + if (KHM_SUCCEEDED(khc_open_space(NULL, + L"CredWindow", + KHM_PERM_READ, + &csp_cw))) { + if (KHM_FAILED(khc_read_int32(csp_cw, + L"RefreshTimeout", + &timeout))) + timeout = MW_REFRESH_TIMEOUT; + khc_close_space(csp_cw); + } else + timeout = MW_REFRESH_TIMEOUT; + + timeout *= 1000; /* convert to milliseconds */ + + SetTimer(hwnd, MW_REFRESH_TIMER, timeout, NULL); +} + +LRESULT CALLBACK khm_main_wnd_proc( + HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ) +{ + LPNMHDR lpnm; + + switch(uMsg) { + case WM_CREATE: + khm_create_main_window_controls(hwnd); + kmq_subscribe_hwnd(KMSG_CRED, hwnd); + kmq_subscribe_hwnd(KMSG_ACT, hwnd); + kmq_subscribe_hwnd(KMSG_KMM, hwnd); + mw_restart_refresh_timer(hwnd); + + if (!kmm_load_pending()) + kmq_post_message(KMSG_ACT, KMSG_ACT_BEGIN_CMDLINE, 0, 0); + break; + + case WM_DESTROY: + kmq_unsubscribe_hwnd(KMSG_ACT, hwnd); + kmq_unsubscribe_hwnd(KMSG_CRED, hwnd); + PostQuitMessage(0); + break; + + case WM_NOTIFY: + lpnm = (LPNMHDR) lParam; + if(lpnm->hwndFrom == khui_main_menu_toolbar) { + return khm_menu_notify_main(lpnm); + } else if(lpnm->hwndFrom == khui_hwnd_standard_toolbar) { + return khm_toolbar_notify(lpnm); + } else if(lpnm->hwndFrom == khm_hwnd_rebar) { + return khm_rebar_notify(lpnm); + } + break; + + case WM_COMMAND: + switch(LOWORD(wParam)) { + /* general actions */ + case KHUI_ACTION_VIEW_REFRESH: + InvalidateRect(khm_hwnd_main_cred, NULL, FALSE); + kmq_post_message(KMSG_CRED, KMSG_CRED_REFRESH, 0, NULL); + return 0; + + case KHUI_ACTION_PASSWD_ID: + khm_cred_change_password(NULL); + return 0; + + case KHUI_ACTION_NEW_CRED: + khm_cred_obtain_new_creds(NULL); + return 0; + + case KHUI_ACTION_RENEW_CRED: + khm_cred_renew_creds(); + return 0; + + case KHUI_ACTION_DESTROY_CRED: + khm_cred_destroy_creds(); + return 0; + + case KHUI_ACTION_SET_DEF_ID: + khm_cred_set_default(); + return 0; + + case KHUI_ACTION_EXIT: + DestroyWindow(hwnd); + break; + + case KHUI_ACTION_OPEN_APP: + khm_show_main_window(); + break; + + case KHUI_ACTION_CLOSE_APP: + khm_hide_main_window(); + break; + + case KHUI_ACTION_OPT_KHIM: + khm_show_config_pane(NULL); + break; + + case KHUI_ACTION_OPT_IDENTS: { + khui_config_node node; + + khui_cfg_open(NULL, L"KhmIdentities", &node); + khm_show_config_pane(node); + } + break; + + case KHUI_ACTION_OPT_NOTIF: { + khui_config_node node; + + khui_cfg_open(NULL, L"KhmNotifications", &node); + khm_show_config_pane(node); + } + break; + + case KHUI_ACTION_HELP_ABOUT: + khm_create_about_window(); + break; + + case KHUI_ACTION_PROPERTIES: + /* properties are not handled by the main window. + Just bounce it to credwnd. However, use SendMessage + instead of PostMessage so we don't lose context */ + return SendMessage(khm_hwnd_main_cred, uMsg, + wParam, lParam); + + /* menu commands */ + case KHUI_PACTION_MENU: + if(HIWORD(lParam) == 1) + mm_last_hot_item = LOWORD(lParam); + return khm_menu_activate(MENU_ACTIVATE_DEFAULT); + + /* generic, retargetting */ + case KHUI_PACTION_UP: + case KHUI_PACTION_UP_TOGGLE: + case KHUI_PACTION_UP_EXTEND: + case KHUI_PACTION_DOWN: + case KHUI_PACTION_DOWN_TOGGLE: + case KHUI_PACTION_DOWN_EXTEND: + case KHUI_PACTION_LEFT: + case KHUI_PACTION_RIGHT: + case KHUI_PACTION_ESC: + case KHUI_PACTION_ENTER: + /* menu tracking */ + if(mm_last_hot_item != -1) { + switch(LOWORD(wParam)) { + case KHUI_PACTION_LEFT: + khm_menu_activate(MENU_ACTIVATE_LEFT); + break; + + case KHUI_PACTION_RIGHT: + khm_menu_activate(MENU_ACTIVATE_RIGHT); + break; + + case KHUI_PACTION_ESC: + case KHUI_PACTION_ENTER: + khm_menu_activate(MENU_ACTIVATE_NONE); + break; + + case KHUI_PACTION_DOWN: + khm_menu_track_current(); + break; + } + return 0; + } + + /*FALLTHROUGH*/ + + case KHUI_PACTION_DELETE: + + case KHUI_ACTION_LAYOUT_ID: + case KHUI_ACTION_LAYOUT_TYPE: + case KHUI_ACTION_LAYOUT_LOC: + /* otherwise fallthrough and bounce to the creds window */ + return SendMessage(khm_hwnd_main_cred, uMsg, + wParam, lParam); + } + break; /* WM_COMMAND */ + + case WM_SYSCOMMAND: + switch(wParam & 0xfff0) { + case SC_MINIMIZE: + khm_hide_main_window(); + return 0; + + case SC_CLOSE: + { + khm_handle csp_cw; + BOOL keep_running = FALSE; + + if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow", + KHM_PERM_READ, &csp_cw))) { + khm_int32 t; + + if (KHM_SUCCEEDED(khc_read_int32(csp_cw, L"KeepRunning", + &t))) + keep_running = t; +#ifdef DEBUG + else + assert(FALSE); +#endif + + khc_close_space(csp_cw); + } +#ifdef DEBUG + else + assert(FALSE); +#endif + + if (keep_running) + khm_hide_main_window(); + else + DestroyWindow(hwnd); + } + return 0; + } + break; + + case WM_MEASUREITEM: + /* sent to measure the bitmaps associated with a menu item */ + if(!wParam) /* sent by menu */ + return khm_menu_measure_item(wParam, lParam); + break; + + case WM_DRAWITEM: + /* sent to draw a menu item */ + if(!wParam) + return khm_menu_draw_item(wParam, lParam); + break; + + case WM_ERASEBKGND: + /* Don't erase the background. The whole client area is + covered with children. It doesn't need to be erased */ + return TRUE; + break; + + case WM_SIZE: + if(hwnd == khm_hwnd_main && + (wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED)) { + int cwidth, cheight; + RECT r_rebar, r_status; + + cwidth = LOWORD(lParam); + cheight = HIWORD(lParam); + + /* resize the rebar control */ + SendMessage(khm_hwnd_rebar, WM_SIZE, 0, 0); + + khui_update_statusbar(hwnd); + + GetWindowRect(khm_hwnd_rebar, &r_rebar); + GetWindowRect(khui_hwnd_statusbar, &r_status); + + /* the cred window fills the area between the rebar + and the status bar */ + MoveWindow(khm_hwnd_main_cred, 0, + r_rebar.bottom - r_rebar.top, + r_status.right - r_status.left, + r_status.top - r_rebar.bottom, TRUE); + + SetTimer(hwnd, + MW_RESIZE_TIMER, + MW_RESIZE_TIMEOUT, + NULL); + return 0; + } + break; + + case WM_MOVE: + { + SetTimer(hwnd, + MW_RESIZE_TIMER, + MW_RESIZE_TIMEOUT, + NULL); + } + break; + + case WM_TIMER: + if (wParam == MW_RESIZE_TIMER) { + RECT r; + khm_handle csp_cw; + khm_handle csp_mw; + + KillTimer(hwnd, wParam); + + GetWindowRect(hwnd, &r); + + if (KHM_SUCCEEDED(khc_open_space(NULL, + L"CredWindow", + KHM_PERM_WRITE, + &csp_cw))) { + if (KHM_SUCCEEDED(khc_open_space(csp_cw, + L"Windows\\Main", + KHM_PERM_WRITE, + &csp_mw))) { + khc_write_int32(csp_mw, L"XPos", r.left); + khc_write_int32(csp_mw, L"YPos", r.top); + khc_write_int32(csp_mw, L"Width", + r.right - r.left); + khc_write_int32(csp_mw, L"Height", + r.bottom - r.top); + + khc_close_space(csp_mw); + } + khc_close_space(csp_cw); + } + } else if (wParam == MW_REFRESH_TIMER) { + kmq_post_message(KMSG_CRED, KMSG_CRED_REFRESH, 0, 0); + } + break; + + case WM_MENUSELECT: + return khm_menu_handle_select(wParam, lParam); + break; + + case KMQ_WM_DISPATCH: + { + kmq_message * m; + khm_int32 rv = KHM_ERROR_SUCCESS; + + kmq_wm_begin(lParam, &m); + if (m->type == KMSG_ACT && + m->subtype == KMSG_ACT_REFRESH) { + khm_update_standard_toolbar(); + } else if (m->type == KMSG_ACT && + m->subtype == KMSG_ACT_BEGIN_CMDLINE) { + khm_cred_begin_commandline(); + } else if (m->type == KMSG_CRED && + m->subtype == KMSG_CRED_REFRESH) { + mw_restart_refresh_timer(hwnd); + } else if (m->type == KMSG_KMM && + m->subtype == KMSG_KMM_I_DONE) { + kmq_post_message(KMSG_ACT, KMSG_ACT_BEGIN_CMDLINE, 0, 0); + } + return kmq_wm_end(m, rv); + } + break; + + case WM_KHUI_ASSIGN_COMMANDLINE: + { + HANDLE hmap; + void * xfer; + wchar_t mapname[256]; + + StringCbPrintf(mapname, sizeof(mapname), + COMMANDLINE_MAP_FMT, (DWORD) lParam); + + hmap = OpenFileMapping(FILE_MAP_READ, FALSE, mapname); + + if (hmap == NULL) + return 1; + + xfer = MapViewOfFile(hmap, FILE_MAP_READ, 0, 0, + sizeof(khm_startup)); + + if (xfer) { + memcpy(&khm_startup, xfer, sizeof(khm_startup)); + + UnmapViewOfFile(xfer); + } + + CloseHandle(hmap); + + if(InSendMessage()) + ReplyMessage(0); + + khm_startup.exit = FALSE; + + khm_startup.seen = FALSE; + khm_startup.processing = FALSE; + + khm_cred_begin_commandline(); + } + break; + } + return DefWindowProc(hwnd,uMsg,wParam,lParam); +} + +LRESULT CALLBACK khm_null_wnd_proc( + HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ) { + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +LRESULT khm_rebar_notify(LPNMHDR lpnm) { + switch(lpnm->code) { + case RBN_AUTOBREAK: + { + LPNMREBARAUTOBREAK lpra = (LPNMREBARAUTOBREAK) lpnm; + lpra->fAutoBreak = TRUE; + } + break; + + case RBN_BEGINDRAG: + { + LPNMREBAR lprb = (LPNMREBAR) lpnm; + if ((lprb->dwMask & RBNM_ID) && + lprb->wID == 0) + return 1; + else + return 0; + } + break; + + case NM_CUSTOMDRAW: + return CDRF_DODEFAULT; + break; + } + + return 1; +} + +void khm_create_main_window_controls(HWND hwnd_main) { + REBARINFO rbi; + HWND hwRebar; + + hwRebar = + CreateWindowEx(WS_EX_TOOLWINDOW, + REBARCLASSNAME, + L"Rebar", + WS_CHILD | + WS_VISIBLE| + WS_CLIPSIBLINGS | + WS_CLIPCHILDREN | + CCS_NODIVIDER | + RBS_VARHEIGHT | + RBS_FIXEDORDER, + 0,0,0,0, + hwnd_main, + NULL, + khm_hInstance, + NULL); + + if(!hwRebar) { + DWORD dwe = GetLastError(); + return; + } + + khm_hwnd_rebar = hwRebar; + + rbi.cbSize = sizeof(rbi); + rbi.fMask = 0; + rbi.himl = (HIMAGELIST) NULL; + if(!SendMessage(hwRebar, RB_SETBARINFO, 0, (LPARAM) &rbi)) + return; + + /* self attach */ + khm_menu_create_main(hwRebar); + khm_create_standard_toolbar(hwRebar); + khui_create_statusbar(hwnd_main); + + /* manual attach */ + khm_hwnd_main_cred = khm_create_credwnd(hwnd_main); +} + +void khm_create_main_window(void) { + wchar_t buf[1024]; + khm_handle csp_cw = NULL; + khm_handle csp_mw = NULL; + int x,y,width,height; + + LoadString(khm_hInstance, IDS_MAIN_WINDOW_TITLE, buf, sizeof(buf)/sizeof(buf[0])); + + khm_hwnd_null = + CreateWindow(MAKEINTATOM(khm_null_window_class), + buf, + 0, /* Style */ + 0, 0, /* x, y */ + 100, 100, /* width, height */ + NULL, /* parent */ + NULL, /* menu */ + NULL, /* HINSTANCE */ + 0); /* lparam */ + + if (!khm_hwnd_null) + return; + + x = CW_USEDEFAULT; + y = CW_USEDEFAULT; + width = CW_USEDEFAULT; + height = CW_USEDEFAULT; + + if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow", + KHM_PERM_READ, + &csp_cw))) { + if (KHM_SUCCEEDED(khc_open_space(csp_cw, + L"Windows\\Main", + KHM_PERM_READ, + &csp_mw))) { + khm_int32 t; + + if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"XPos", &t))) + x = t; + if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"YPos", &t))) + y = t; + if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"Width", &t))) + width = t; + if (KHM_SUCCEEDED(khc_read_int32(csp_mw, L"Height", &t))) + height = t; + + khc_close_space(csp_mw); + } + khc_close_space(csp_cw); + } + + khm_hwnd_main = + CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, + MAKEINTATOM(khm_main_window_class), + buf, + WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | + WS_CLIPSIBLINGS, + x, y, width, height, + khm_hwnd_null, + NULL, + NULL, + NULL); + + if (!khm_hwnd_main) + return; +} + +void khm_show_main_window(void) { + + if (khm_nCmdShow == SW_RESTORE) { + HWND hw; + + hw = GetForegroundWindow(); + if (hw != khm_hwnd_main) + SetForegroundWindow(khm_hwnd_main); + } + + ShowWindow(khm_hwnd_main, khm_nCmdShow); + UpdateWindow(khm_hwnd_main); + + khm_nCmdShow = SW_RESTORE; +} + +void khm_hide_main_window(void) { + khm_handle csp_notices = NULL; + khm_int32 show_warning = FALSE; + + if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow\\Notices", + KHM_PERM_WRITE, &csp_notices)) && + KHM_SUCCEEDED(khc_read_int32(csp_notices, L"MinimizeWarning", + &show_warning)) && + show_warning != 0) { + khui_alert * alert; + wchar_t title[KHUI_MAXCCH_TITLE]; + wchar_t msg[KHUI_MAXCCH_MESSAGE]; + + LoadString(khm_hInstance, IDS_WARN_WM_TITLE, + title, ARRAYLENGTH(title)); + LoadString(khm_hInstance, IDS_WARN_WM_MSG, + msg, ARRAYLENGTH(msg)); + + khui_alert_create_simple(title, msg, KHERR_INFO, &alert); + khui_alert_set_flags(alert, KHUI_ALERT_FLAG_REQUEST_BALLOON, + KHUI_ALERT_FLAG_REQUEST_BALLOON); + + khui_alert_show(alert); + + khc_write_int32(csp_notices, L"MinimizeWarning", 0); + } + + if (csp_notices != NULL) + khc_close_space(csp_notices); + + ShowWindow(khm_hwnd_main, SW_HIDE); +} + +BOOL khm_is_main_window_visible(void) { + return IsWindowVisible(khm_hwnd_main); +} + +BOOL khm_is_main_window_active(void) { + if (!IsWindowVisible(khm_hwnd_main)) + return FALSE; + if (GetForegroundWindow() == khm_hwnd_main) + return TRUE; + return khm_is_dialog_active(); +} + +void khm_register_main_wnd_class(void) { + WNDCLASSEX wc; + + wc.cbSize = sizeof(WNDCLASSEX); + wc.style = 0; + wc.lpfnWndProc = khm_null_wnd_proc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = khm_hInstance; + wc.hIcon = LoadIcon(khm_hInstance, MAKEINTRESOURCE(IDI_MAIN_APP)); + wc.hCursor = LoadCursor((HINSTANCE) NULL, MAKEINTRESOURCE(IDC_ARROW)); + wc.hIconSm = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_MAIN_APP), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE); + wc.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE); + wc.lpszMenuName = NULL; + wc.lpszClassName = KHUI_NULL_WINDOW_CLASS; + + khm_null_window_class = RegisterClassEx(&wc); + + + wc.cbSize = sizeof(WNDCLASSEX); + wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = khm_main_wnd_proc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = khm_hInstance; + wc.hIcon = LoadIcon(khm_hInstance, MAKEINTRESOURCE(IDI_MAIN_APP)); + wc.hCursor = LoadCursor((HINSTANCE) NULL, MAKEINTRESOURCE(IDC_ARROW)); + wc.hIconSm = LoadImage(khm_hInstance, MAKEINTRESOURCE(IDI_MAIN_APP), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE); + wc.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE); + wc.lpszMenuName = NULL; + wc.lpszClassName = KHUI_MAIN_WINDOW_CLASS; + + khm_main_window_class = RegisterClassEx(&wc); +} + +void khm_unregister_main_wnd_class(void) { + UnregisterClass(MAKEINTATOM(khm_main_window_class),khm_hInstance); + UnregisterClass(MAKEINTATOM(khm_null_window_class),khm_hInstance); +} diff --git a/src/windows/identity/ui/mainwnd.h b/src/windows/identity/ui/mainwnd.h new file mode 100644 index 000000000..cdaac1e96 --- /dev/null +++ b/src/windows/identity/ui/mainwnd.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_MAINWND_H +#define __KHIMAIRA_MAINWND_H + +#define KHUI_MAIN_WINDOW_CLASS L"KhmMainWindowClass" +#define KHUI_NULL_WINDOW_CLASS L"KhmNullWindowClass" + +extern ATOM khm_main_window_class; +extern HWND khm_hwnd_main; +extern HWND khm_hwnd_rebar; + +void khm_register_main_wnd_class(void); +void khm_unregister_main_wnd_class(void); +void khm_create_main_window_controls(HWND); +void khm_create_main_window(void); +void khm_show_main_window(void); +void khm_hide_main_window(void); +BOOL khm_is_main_window_visible(void); +BOOL khm_is_main_window_active(void); +LRESULT khm_rebar_notify(LPNMHDR lpnm); + +void +khm_set_dialog_result(HWND hwnd, LRESULT lr); + +LRESULT CALLBACK +khm_main_wnd_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +#define WM_KHUI_ASSIGN_COMMANDLINE 32808 + +#define COMMANDLINE_MAP_FMT L"Local\\NetIDMgr_Cmdline_%lu" + +#endif diff --git a/src/windows/identity/ui/makeacceldef.pl b/src/windows/identity/ui/makeacceldef.pl new file mode 100644 index 000000000..f13a3ec18 --- /dev/null +++ b/src/windows/identity/ui/makeacceldef.pl @@ -0,0 +1,29 @@ +# + +die "Please specify input and output filenames" if($#ARGV != 1); + +open INF, '<', $ARGV[0] or die "Can't open input file"; +open OUF, '>', $ARGV[1] or die "Can't open output file"; + +print OUF <<EOS; +#include<khimaira.h> + + khui_accel_def khui_accel_global[] = { +EOS + +# skip first line + <INF>; + +while(<INF>) { + print OUF "{".$_."},\n"; +} + +print OUF <<EOS; +}; + +int khui_n_accel_global = sizeof(khui_accel_global) / sizeof(khui_accel_def); + +EOS + +close INF; +close OUF; diff --git a/src/windows/identity/ui/makeactiondef.pl b/src/windows/identity/ui/makeactiondef.pl new file mode 100644 index 000000000..a83325b3a --- /dev/null +++ b/src/windows/identity/ui/makeactiondef.pl @@ -0,0 +1,29 @@ +# + +die "Please specify input and output filenames" if($#ARGV != 1); + +open INF, '<', $ARGV[0] or die "Can't open input file"; +open OUF, '>', $ARGV[1] or die "Can't open output file"; + +print OUF <<EOS; +#include<khimaira.h> + + khui_action khui_actions[] = { +EOS + +# skip first line + <INF>; + +while(<INF>) { + print OUF "{".$_."},\n"; +} + +print OUF <<EOS; +}; + +int khui_n_actions = sizeof(khui_actions) / sizeof(khui_action); + +EOS + +close INF; +close OUF; diff --git a/src/windows/identity/ui/netidmgr.exe.manifest.i386 b/src/windows/identity/ui/netidmgr.exe.manifest.i386 new file mode 100644 index 000000000..5e83258c4 --- /dev/null +++ b/src/windows/identity/ui/netidmgr.exe.manifest.i386 @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> +<assemblyIdentity + version="1.0.0.0" + processorArchitecture="X86" + name="MIT.NetIDMgr.UI" + type="win32" +/> +<description>Khimaira Credentials Manager</description> +<dependency> + <dependentAssembly> + <assemblyIdentity + type="win32" + name="Microsoft.Windows.Common-Controls" + version="6.0.0.0" + processorArchitecture="X86" + publicKeyToken="6595b64144ccf1df" + language="*" + /> + </dependentAssembly> +</dependency> +</assembly> diff --git a/src/windows/identity/ui/netidmgr.manifest.i386.vc7 b/src/windows/identity/ui/netidmgr.manifest.i386.vc7 new file mode 100644 index 000000000..5e83258c4 --- /dev/null +++ b/src/windows/identity/ui/netidmgr.manifest.i386.vc7 @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> +<assemblyIdentity + version="1.0.0.0" + processorArchitecture="X86" + name="MIT.NetIDMgr.UI" + type="win32" +/> +<description>Khimaira Credentials Manager</description> +<dependency> + <dependentAssembly> + <assemblyIdentity + type="win32" + name="Microsoft.Windows.Common-Controls" + version="6.0.0.0" + processorArchitecture="X86" + publicKeyToken="6595b64144ccf1df" + language="*" + /> + </dependentAssembly> +</dependency> +</assembly> diff --git a/src/windows/identity/ui/netidmgr.manifest.i386.vc7.debug b/src/windows/identity/ui/netidmgr.manifest.i386.vc7.debug new file mode 100644 index 000000000..5e83258c4 --- /dev/null +++ b/src/windows/identity/ui/netidmgr.manifest.i386.vc7.debug @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> +<assemblyIdentity + version="1.0.0.0" + processorArchitecture="X86" + name="MIT.NetIDMgr.UI" + type="win32" +/> +<description>Khimaira Credentials Manager</description> +<dependency> + <dependentAssembly> + <assemblyIdentity + type="win32" + name="Microsoft.Windows.Common-Controls" + version="6.0.0.0" + processorArchitecture="X86" + publicKeyToken="6595b64144ccf1df" + language="*" + /> + </dependentAssembly> +</dependency> +</assembly> diff --git a/src/windows/identity/ui/netidmgr.manifest.i386.vc8 b/src/windows/identity/ui/netidmgr.manifest.i386.vc8 new file mode 100644 index 000000000..84d91a339 --- /dev/null +++ b/src/windows/identity/ui/netidmgr.manifest.i386.vc8 @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> +<assemblyIdentity + version="1.0.0.0" + processorArchitecture="X86" + name="MIT.NetIDMgr.UI" + type="win32" +/> +<description>Khimaira Credentials Manager</description> +<dependency> + <dependentAssembly> + <assemblyIdentity + type="win32" + name="Microsoft.Windows.Common-Controls" + version="6.0.0.0" + processorArchitecture="X86" + publicKeyToken="6595b64144ccf1df" + language="*" + /> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity + type="win32" + name="Microsoft.VC80.DebugCRT" + version="8.0.50215.4652" + processorArchitecture="x86" + publicKeyToken="1fc8b3b9a1e18e3b" + /> + </dependentAssembly> +</dependency> +</assembly> diff --git a/src/windows/identity/ui/netidmgr.manifest.i386.vc8.debug b/src/windows/identity/ui/netidmgr.manifest.i386.vc8.debug new file mode 100644 index 000000000..84d91a339 --- /dev/null +++ b/src/windows/identity/ui/netidmgr.manifest.i386.vc8.debug @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> +<assemblyIdentity + version="1.0.0.0" + processorArchitecture="X86" + name="MIT.NetIDMgr.UI" + type="win32" +/> +<description>Khimaira Credentials Manager</description> +<dependency> + <dependentAssembly> + <assemblyIdentity + type="win32" + name="Microsoft.Windows.Common-Controls" + version="6.0.0.0" + processorArchitecture="X86" + publicKeyToken="6595b64144ccf1df" + language="*" + /> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity + type="win32" + name="Microsoft.VC80.DebugCRT" + version="8.0.50215.4652" + processorArchitecture="x86" + publicKeyToken="1fc8b3b9a1e18e3b" + /> + </dependentAssembly> +</dependency> +</assembly> diff --git a/src/windows/identity/ui/newcredwnd.c b/src/windows/identity/ui/newcredwnd.c new file mode 100644 index 000000000..4b6ce08bf --- /dev/null +++ b/src/windows/identity/ui/newcredwnd.c @@ -0,0 +1,1694 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khmapp.h> +#include<assert.h> + +ATOM khui_newcredwnd_cls; + +/* forward dcl */ +static void +nc_position_credtext(khui_nc_wnd_data * d); + +/* Common dialog procedure. Be careful. This is used by more than + one dialog. */ +static INT_PTR CALLBACK +nc_common_dlg_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + switch(uMsg) { + case WM_INITDIALOG: + +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, DWLP_USER, lParam); +#pragma warning(pop) + + return TRUE; + + case WM_COMMAND: + { + int ctrl_id; + + ctrl_id = LOWORD(wParam); + if (ctrl_id < KHUI_CW_ID_MIN || + ctrl_id > KHUI_CW_ID_MAX) { + /* pump it to the parent */ + PostMessage(GetParent(hwnd), WM_COMMAND, wParam, lParam); + return TRUE; + } /* else we allow the message to fall through and get + passed into the identity provider's message + handler. */ + } + break; + +#if 0 + /* someday this will be used to draw custom tab buttons. But + that's not today */ + case WM_DRAWITEM: + { + khui_nc_wnd_data * d; + int id; + LPDRAWITEMSTRUCT ds; + + d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER); + id = wParam; + ds = (LPDRAWITEMSTRUCT) lParam; + + if(id >= NC_TS_CTRL_ID_MIN && id <= NC_TS_CTRL_ID_MAX) { + /*TODO: custom draw the buttons */ + } + else + return FALSE; + } + break; +#endif + + case KHUI_WM_NC_NOTIFY: + { + khui_nc_wnd_data * d; + d = (khui_nc_wnd_data *)(LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + + /* message sent by parent to notify us of something */ + switch(HIWORD(wParam)) { + case WMNC_DIALOG_EXPAND: + if(hwnd == d->dlg_main) { + HWND hw; + + if(hw = GetDlgItem(hwnd, IDOK)) + ShowWindow(hw, SW_HIDE); + if(hw = GetDlgItem(hwnd, IDCANCEL)) + ShowWindow(hw, SW_HIDE); + if(hw = GetDlgItem(hwnd, IDC_NC_OPTIONS)) + ShowWindow(hw, SW_HIDE); + + d->r_credtext.bottom = d->r_area.bottom; + + nc_position_credtext(d); + + return TRUE; + } + } + } + return TRUE; + } + + /* check if we have a wnd_data, and if so pass the message on to + the identity provider callback. */ + { + khui_nc_wnd_data * d; + + d = (khui_nc_wnd_data *) (LONG_PTR) + GetWindowLongPtr(hwnd, DWLP_USER); + + if (d && d->nc && d->nc->ident_cb) { + return d->nc->ident_cb(d->nc, WMNC_IDENT_WMSG, hwnd, uMsg, + wParam, lParam); + } + } + + return FALSE; +} + +static void +nc_position_credtext(khui_nc_wnd_data * d) +{ + HWND hw; + + hw = GetDlgItem(d->dlg_main, IDC_NC_CREDTEXT); +#ifdef DEBUG + assert(hw); +#endif + + if (d->r_credtext.bottom < d->r_credtext.top + d->r_row.bottom * 2) { + /* not enough room */ + if (d->nc->mode == KHUI_NC_MODE_MINI && + d->nc->subtype != KMSG_CRED_PASSWORD) { + PostMessage(d->nc->hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_DIALOG_EXPAND), 0); + return; + } else { + ShowWindow(hw, SW_HIDE); + return; + } + } else { + ShowWindow(hw, SW_SHOW); + } + + SetWindowPos(hw, NULL, + d->r_credtext.left + d->r_n_input.left, /* x */ + d->r_credtext.top, /* y */ + d->r_n_input.right - d->r_n_input.left, /* width */ + d->r_credtext.bottom - d->r_credtext.top, /* height */ + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOZORDER); + + hw = GetDlgItem(d->dlg_main, IDC_NC_CREDTEXT_LABEL); + + SetWindowPos(hw, NULL, + d->r_credtext.left + d->r_n_label.left, /* x */ + d->r_credtext.top, /* y */ + d->r_n_label.right - d->r_n_label.left, /* width */ + d->r_n_label.bottom - d->r_n_label.top, /* height */ + SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOZORDER); +} + +/* sorts tab buttons */ +static int __cdecl +nc_tab_sort_func(const void * v1, const void * v2) +{ + /* v1 and v2 and of type : khui_new_creds_by_type ** */ + khui_new_creds_by_type *t1, *t2; + + t1 = *((khui_new_creds_by_type **) v1); + t2 = *((khui_new_creds_by_type **) v2); + + if(t1->ordinal > 0) { + if(t2->ordinal > 0) { + if(t1->ordinal == t2->ordinal) + return wcscmp(t1->name, t2->name); + else + /* safe to convert to an int here */ + return (int) (t1->ordinal - t2->ordinal); + } else + return -1; + } else { + if(t2->ordinal > 0) + return 1; + else if (t1->name && t2->name) + return wcscmp(t1->name, t2->name); + else + return 0; + } +} + +static void +nc_notify_types_async(khui_new_creds * c, UINT uMsg, + WPARAM wParam, LPARAM lParam) +{ + khm_size i; + + for(i=0; i<c->n_types; i++) { + PostMessage(c->types[i]->hwnd_panel, uMsg, wParam, lParam); + } +} + +static void +nc_notify_types(khui_new_creds * c, UINT uMsg, + WPARAM wParam, LPARAM lParam) +{ + khm_size i; + + for(i=0; i<c->n_types; i++) { + SendMessage(c->types[i]->hwnd_panel, uMsg, wParam, lParam); + } +} + +#define NC_MAXCCH_CREDTEXT 16384 +#define NC_MAXCB_CREDTEXT (NC_MAXCCH_CREDTEXT * sizeof(wchar_t)) + +static void +nc_update_credtext(khui_nc_wnd_data * d) +{ + wchar_t * ctbuf = NULL; + wchar_t * buf; + BOOL okEnable = FALSE; + BOOL validId = FALSE; + HWND hw = NULL; + size_t cch = 0; + + ctbuf = malloc(NC_MAXCB_CREDTEXT); + + assert(ctbuf != NULL); + + LoadString(khm_hInstance, IDS_NC_CREDTEXT_TABS, ctbuf, NC_MAXCCH_CREDTEXT); + StringCchLength(ctbuf, NC_MAXCCH_CREDTEXT, &cch); + buf = ctbuf + cch; + nc_notify_types(d->nc, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_UPDATE_CREDTEXT), 0); + + /* hopefully all the types have updated their credential texts */ + if(d->nc->n_identities == 1) { + wchar_t main_fmt[256]; + wchar_t id_fmt[256]; + wchar_t id_name[KCDB_IDENT_MAXCCH_NAME]; + wchar_t id_string[KCDB_IDENT_MAXCCH_NAME + 256]; + khm_size cbbuf; + khm_int32 flags; + + + LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_ONE, + main_fmt, (int) ARRAYLENGTH(main_fmt)); + + cbbuf = sizeof(id_name); + kcdb_identity_get_name(d->nc->identities[0], id_name, &cbbuf); + + kcdb_identity_get_flags(d->nc->identities[0], &flags); + if (flags & KCDB_IDENT_FLAG_INVALID) { + LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_INVALID, + id_fmt, (int) ARRAYLENGTH(id_fmt)); + } else if(flags & KCDB_IDENT_FLAG_VALID) { + LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_VALID, + id_fmt, (int) ARRAYLENGTH(id_fmt)); + } else if(d->nc->subtype == KMSG_CRED_NEW_CREDS) { + LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_CHECKING, + id_fmt, (int) ARRAYLENGTH(id_fmt)); + } else { + LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_UNCHECKED, + id_fmt, (int) ARRAYLENGTH(id_fmt)); + } + + StringCbPrintf(id_string, sizeof(id_string), id_fmt, id_name); + + StringCbPrintf(buf, NC_MAXCB_CREDTEXT - cch*sizeof(wchar_t), + main_fmt, id_string); + + } else if(d->nc->n_identities > 1) { + wchar_t *ids_string; + khm_size cb_ids_string; + + wchar_t id_name[KCDB_IDENT_MAXCCH_NAME]; + wchar_t id_fmt[256]; + wchar_t id_string[KCDB_IDENT_MAXCCH_NAME + 256]; + + wchar_t main_fmt[256]; + khm_size cbbuf; + + LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_MANY, + main_fmt, (int) ARRAYLENGTH(main_fmt)); + + /* we are going to concatenate all the identity names into + a comma separated string */ + + /* d->nc->n_identities is at least 2 */ + ids_string = malloc((KCDB_IDENT_MAXCB_NAME + sizeof(id_fmt)) * + (d->nc->n_identities - 1)); + cb_ids_string = + (KCDB_IDENT_MAXCB_NAME + sizeof(id_fmt)) * + (d->nc->n_identities - 1); + + assert(ids_string != NULL); + + ids_string[0] = 0; + + { + khm_size i; + khm_int32 flags; + + for(i=1; i<d->nc->n_identities; i++) { + if(i>1) { + StringCbCat(ids_string, cb_ids_string, L","); + } + + flags = 0; + + cbbuf = sizeof(id_name); + kcdb_identity_get_name(d->nc->identities[i], id_name, &cbbuf); + kcdb_identity_get_flags(d->nc->identities[i], &flags); + if(flags & KCDB_IDENT_FLAG_INVALID) { + LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_INVALID, + id_fmt, (int) ARRAYLENGTH(id_fmt)); + } else if(flags & KCDB_IDENT_FLAG_VALID) { + LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_VALID, + id_fmt, (int) ARRAYLENGTH(id_fmt)); + } else { + LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_UNCHECKED, + id_fmt, (int) ARRAYLENGTH(id_fmt)); + } + + StringCbPrintf(id_string, sizeof(id_string), id_fmt, id_name); + StringCbCat(ids_string, cb_ids_string, id_string); + } + + cbbuf = sizeof(id_name); + kcdb_identity_get_name(d->nc->identities[0], id_name, &cbbuf); + kcdb_identity_get_flags(d->nc->identities[0], &flags); + if(flags & KCDB_IDENT_FLAG_INVALID) { + LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_INVALID, + id_fmt, (int) ARRAYLENGTH(id_fmt)); + } else if(flags & KCDB_IDENT_FLAG_VALID) { + LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_VALID, + id_fmt, (int) ARRAYLENGTH(id_fmt)); + } else { + LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_UNCHECKED, + id_fmt, (int) ARRAYLENGTH(id_fmt)); + } + StringCbPrintf(id_string, sizeof(id_string), id_fmt, id_name); + + StringCbPrintf(buf, NC_MAXCB_CREDTEXT - cch*sizeof(wchar_t), + main_fmt, id_string, ids_string); + + free(ids_string); + } + } else { + LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_NONE, + buf, (int)(NC_MAXCCH_CREDTEXT - cch)); + } + + /* now, append the credtext string from each of the cred types */ + { + khm_size i; + size_t cb; + wchar_t * buf; + + cb = NC_MAXCB_CREDTEXT; + buf = ctbuf; + + for(i=0; i<d->nc->n_types; i++) { + if(d->nc->types[i]->credtext != NULL) { + StringCbCatEx(buf, cb, + d->nc->types[i]->credtext, + &buf, &cb, + 0); + } + } + } + + SetDlgItemText(d->dlg_main, IDC_NC_CREDTEXT, ctbuf); + + free(ctbuf); + + /* so depending on whether the primary identity was found to be + invalid, we need to disable the Ok button and set the title to + reflect this */ + + if(d->nc->n_identities > 0) { + khm_int32 flags = 0; + + if(KHM_SUCCEEDED(kcdb_identity_get_flags(d->nc->identities[0], + &flags)) && + (flags & KCDB_IDENT_FLAG_VALID)) { + validId = TRUE; + } + } + + if (d->nc->window_title == NULL) { + if(validId) { + wchar_t wpostfix[256]; + wchar_t wtitle[KCDB_IDENT_MAXCCH_NAME + 256]; + khm_size cbsize; + + cbsize = sizeof(wtitle); + kcdb_identity_get_name(d->nc->identities[0], wtitle, &cbsize); + + if (d->nc->subtype == KMSG_CRED_PASSWORD) + LoadString(khm_hInstance, IDS_WTPOST_PASSWORD, + wpostfix, (int) ARRAYLENGTH(wpostfix)); + else + LoadString(khm_hInstance, IDS_WTPOST_NEW_CREDS, + wpostfix, (int) ARRAYLENGTH(wpostfix)); + + StringCbCat(wtitle, sizeof(wtitle), wpostfix); + + SetWindowText(d->nc->hwnd, wtitle); + } else { + wchar_t wtitle[256]; + + if (d->nc->subtype == KMSG_CRED_PASSWORD) + LoadString(khm_hInstance, IDS_WT_PASSWORD, + wtitle, (int) ARRAYLENGTH(wtitle)); + else + LoadString(khm_hInstance, IDS_WT_NEW_CREDS, + wtitle, (int) ARRAYLENGTH(wtitle)); + + SetWindowText(d->nc->hwnd, wtitle); + } + } + + if(validId || d->nc->subtype == KMSG_CRED_PASSWORD) { + /* TODO: check if all the required fields have valid values + before enabling the Ok button */ + okEnable = TRUE; + } + + hw = GetDlgItem(d->dlg_main, IDOK); + EnableWindow(hw, okEnable); + hw = GetDlgItem(d->dlg_bb, IDOK); + EnableWindow(hw, okEnable); +} + +#define CW_PARAM DWLP_USER + +static LRESULT +nc_handle_wm_create(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + LPCREATESTRUCT lpc; + khui_new_creds * c; + khui_nc_wnd_data * ncd; + int x, y; + int width, height; + RECT r; + + lpc = (LPCREATESTRUCT) lParam; + + ncd = malloc(sizeof(*ncd)); + ZeroMemory(ncd, sizeof(*ncd)); + + c = (khui_new_creds *) lpc->lpCreateParams; + ncd->nc = c; + c->hwnd = hwnd; + +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, CW_PARAM, (LONG_PTR) ncd); +#pragma warning(pop) + + /* first try to create the main dialog panel */ + + assert(c->subtype == KMSG_CRED_NEW_CREDS || + c->subtype == KMSG_CRED_PASSWORD); + + ncd->dlg_main = CreateDialogParam(khm_hInstance, + MAKEINTRESOURCE(IDD_NC_PASSWORD), + hwnd, + nc_common_dlg_proc, + (LPARAM) ncd); +#ifdef DEBUG + assert(ncd->dlg_main); +#endif + + { + RECT r_main; + RECT r_area; + RECT r_row; + HWND hw; + + /* pick out metrics for use by the custom prompter stuff */ + GetWindowRect(ncd->dlg_main, &r_main); + + hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_PANEL); +#ifdef DEBUG + assert(hw); +#endif + GetWindowRect(hw, &r_area); + OffsetRect(&r_area,-r_main.left, -r_main.top); + CopyRect(&ncd->r_area, &r_area); + + hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_ROW); +#ifdef DEBUG + assert(hw); +#endif + GetWindowRect(hw, &r); + CopyRect(&r_row, &r); + OffsetRect(&r,-r.left, -r.top); + CopyRect(&ncd->r_row, &r); + + hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_LABEL); +#ifdef DEBUG + assert(hw); +#endif + GetWindowRect(hw, &r); + OffsetRect(&r,-r_row.left, -r_row.top); + CopyRect(&ncd->r_n_label, &r); + + hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_INPUT); +#ifdef DEBUG + assert(hw); +#endif + GetWindowRect(hw, &r); + OffsetRect(&r, -r_row.left, -r_row.top); + CopyRect(&ncd->r_n_input, &r); + + hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_ROW_LG); +#ifdef DEBUG + assert(hw); +#endif + GetWindowRect(hw, &r_row); + + hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_LABEL_LG); +#ifdef DEBUG + assert(hw); +#endif + GetWindowRect(hw, &r); + OffsetRect(&r, -r_row.left, -r_row.top); + CopyRect(&ncd->r_e_label, &r); + + hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_INPUT_LG); +#ifdef DEBUG + assert(hw); +#endif + GetWindowRect(hw, &r); + OffsetRect(&r, -r_row.left, -r_row.top); + CopyRect(&ncd->r_e_input, &r); + + CopyRect(&ncd->r_credtext, &ncd->r_area); + CopyRect(&ncd->r_idspec, &ncd->r_area); + + ncd->r_idspec.bottom = ncd->r_idspec.top; + + hw = GetDlgItem(ncd->dlg_main, IDC_NC_CREDTEXT); +#ifdef DEBUG + assert(hw); +#endif + GetWindowRect(hw, &r); + OffsetRect(&r, -r_main.left, -r_main.top); + ncd->r_credtext.bottom = r.bottom; + } + + /* if the mode is 'mini'*/ + r.left = 0; + r.top = 0; + if(c->mode == KHUI_NC_MODE_MINI) { + r.right = NCDLG_WIDTH; + r.bottom = NCDLG_HEIGHT; + } else { + r.right = NCDLG_WIDTH + NCDLG_BBAR_WIDTH; + r.bottom = NCDLG_HEIGHT + NCDLG_TAB_HEIGHT; + } + + MapDialogRect(ncd->dlg_main, &r); + + ncd->r_main.left = 0; + ncd->r_main.top = 0; + ncd->r_main.right = NCDLG_WIDTH; + ncd->r_main.bottom = NCDLG_HEIGHT; + + ncd->r_ts.left = 0; + ncd->r_ts.top = ncd->r_main.bottom; + ncd->r_ts.right = ncd->r_main.right; + ncd->r_ts.bottom = ncd->r_ts.top + NCDLG_TAB_HEIGHT; + + ncd->r_bb.left = ncd->r_main.right; + ncd->r_bb.top = 0; + ncd->r_bb.right = ncd->r_bb.left + NCDLG_BBAR_WIDTH; + ncd->r_bb.bottom = ncd->r_ts.bottom; + + MapDialogRect(ncd->dlg_main, &(ncd->r_main)); + MapDialogRect(ncd->dlg_main, &(ncd->r_ts)); + MapDialogRect(ncd->dlg_main, &(ncd->r_bb)); + + /* center the new creds window over the main NetIDMgr window */ + width = r.right - r.left; + height = r.bottom - r.top; + + /* adjust width and height to accomodate NC area */ + { + RECT wr,cr; + + GetWindowRect(hwnd, &wr); + GetClientRect(hwnd, &cr); + + /* the non-client and client areas have already been calculated + at this point. We just use the difference to adjust the width + and height */ + width += (wr.right - wr.left) - (cr.right - cr.left); + height += (wr.bottom - wr.top) - (cr.bottom - cr.top); + } + + GetWindowRect(lpc->hwndParent, &r); + x = (r.right + r.left)/2 - width / 2; + y = (r.top + r.bottom)/2 - height / 2; + + MoveWindow(hwnd, x, y, width, height, FALSE); + + SetWindowPos(ncd->dlg_main, + NULL, + ncd->r_main.left, + ncd->r_main.top, + ncd->r_main.right - ncd->r_main.left, + ncd->r_main.bottom - ncd->r_main.top, + SWP_DEFERERASE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOREDRAW | SWP_NOZORDER); + + /* IDD_NC_BBAR is the button bar that sits on the right of the + dialog when the new creds window is in 'expanded' mode. */ + + ncd->dlg_bb = CreateDialogParam(khm_hInstance, + MAKEINTRESOURCE(IDD_NC_BBAR), + hwnd, + nc_common_dlg_proc, + (LPARAM) ncd); + +#ifdef DEBUG + assert(ncd->dlg_bb); +#endif + + SetWindowPos(ncd->dlg_bb, + NULL, + ncd->r_bb.left, + ncd->r_bb.top, + ncd->r_bb.right - ncd->r_bb.left, + ncd->r_bb.bottom - ncd->r_bb.top, + SWP_DEFERERASE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOREDRAW | SWP_NOZORDER); + + /* IDD_NC_TS is the tab strip that sits below the main panel when + the new creds window is in 'expanded' mode */ + + ncd->dlg_ts = CreateDialogParam(khm_hInstance, + MAKEINTRESOURCE(IDD_NC_TS), + hwnd, + nc_common_dlg_proc, + (LPARAM) ncd); + +#ifdef DEBUG + assert(ncd->dlg_ts); +#endif + + SetWindowPos(ncd->dlg_ts, + NULL, + ncd->r_ts.left, + ncd->r_ts.top, + ncd->r_ts.right - ncd->r_ts.left, + ncd->r_ts.bottom - ncd->r_ts.top, + SWP_DEFERERASE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | + SWP_NOREDRAW | SWP_NOZORDER); + + if(c->mode == KHUI_NC_MODE_MINI) { + /* hide and show stuff */ + ShowWindow(ncd->dlg_main, SW_SHOW); + ShowWindow(ncd->dlg_bb, SW_HIDE); + ShowWindow(ncd->dlg_ts, SW_HIDE); + + nc_position_credtext(ncd); + } else { + /* hide and show stuff */ + ShowWindow(ncd->dlg_main, SW_SHOW); + ShowWindow(ncd->dlg_bb, SW_SHOW); + ShowWindow(ncd->dlg_ts, SW_SHOW); + + PostMessage(ncd->dlg_main, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_DIALOG_EXPAND), 0); + } + + /* Call the identity provider callback to set the identity + selector controls */ + c->ident_cb(c, WMNC_IDENT_INIT, NULL, 0, 0, (LPARAM) ncd->dlg_main); + + /* we defer the creation of the tab buttons for later */ + + /* add this to the dialog chain */ + khm_add_dialog(hwnd); + + return TRUE; +} + +static void +nc_add_control_row(khui_nc_wnd_data * d, + HWND label, + HWND input, + khui_control_size size) +{ + RECT r_row; + RECT r_label; + RECT r_input; + HFONT hf; + + hf = (HFONT) SendMessage(d->dlg_main, WM_GETFONT, 0, 0); + SendMessage(label, WM_SETFONT, (WPARAM) hf, FALSE); + SendMessage(input, WM_SETFONT, (WPARAM) hf, FALSE); + + CopyRect(&r_row, &d->r_row); + OffsetRect(&r_row, d->r_idspec.left, d->r_idspec.bottom); + + if (size == KHUI_CTRLSIZE_SMALL) { + CopyRect(&r_label, &d->r_n_label); + CopyRect(&r_input, &d->r_n_input); + OffsetRect(&r_label, r_row.left, r_row.top); + OffsetRect(&r_input, r_row.left, r_row.top); + } else if (size == KHUI_CTRLSIZE_HALF) { + CopyRect(&r_label, &d->r_e_label); + CopyRect(&r_input, &d->r_e_input); + OffsetRect(&r_label, r_row.left, r_row.top); + OffsetRect(&r_input, r_row.left, r_row.top); + } else if (size == KHUI_CTRLSIZE_FULL) { + CopyRect(&r_label, &d->r_n_label); + r_label.right = d->r_row.right; + CopyRect(&r_input, &d->r_n_input); + OffsetRect(&r_input, r_row.left, r_row.top); + OffsetRect(&r_input, 0, r_input.bottom); + r_row.bottom += r_input.bottom; + OffsetRect(&r_label, r_row.left, r_row.top); + } else { +#ifdef DEBUG + assert(FALSE); +#else + return; +#endif + } + + SetWindowPos(label, + ((d->hwnd_last_idspec != NULL)? + d->hwnd_last_idspec: + HWND_TOP), + r_label.left, r_label.top, + r_label.right - r_label.left, + r_label.bottom - r_label.top, + SWP_DEFERERASE | SWP_NOACTIVATE | + SWP_NOOWNERZORDER); + + SetWindowPos(input, + label, + r_input.left, r_input.top, + r_input.right - r_input.left, + r_input.bottom - r_input.top, + SWP_DEFERERASE | SWP_NOACTIVATE | + SWP_NOOWNERZORDER); + + d->hwnd_last_idspec = input; + + d->r_idspec.bottom = r_row.bottom; + + d->r_credtext.top = r_row.bottom; + + nc_position_credtext(d); +} + + +static LRESULT +nc_handle_wm_destroy(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + khui_nc_wnd_data * d; + khm_size i; + + /* remove self from dialog chain */ + khm_del_dialog(hwnd); + + d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM); + + d->nc->ident_cb(d->nc, WMNC_IDENT_EXIT, NULL, 0, 0, 0); + + if(d->hwnd_tc_main) + DestroyWindow(d->hwnd_tc_main); + for(i=0;i<d->nc->n_types;i++) { + if(d->nc->types[i]->hwnd_tc) { + DestroyWindow(d->nc->types[i]->hwnd_tc); + d->nc->types[i]->hwnd_tc = NULL; + } + } + + if(d->dlg_bb) + DestroyWindow(d->dlg_bb); + if(d->dlg_main) + DestroyWindow(d->dlg_main); + if(d->dlg_ts) + DestroyWindow(d->dlg_ts); + + d->dlg_bb = NULL; + d->dlg_main = NULL; + d->dlg_ts = NULL; + + free(d); + + return TRUE; +} + +static LRESULT +nc_handle_wm_command(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + khui_nc_wnd_data * d; + int id; + + d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM); + + switch(HIWORD(wParam)) { + case BN_CLICKED: + switch(LOWORD(wParam)) { + + case IDOK: + d->nc->result = KHUI_NC_RESULT_GET_CREDS; + + /* fallthrough */ + + case IDCANCEL: + /* the default value for d->nc->result is set to + KHUI_NC_RESULT_CANCEL */ + d->nc->response = 0; + + nc_notify_types(d->nc, + KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0,WMNC_DIALOG_PREPROCESS), + 0); + + khui_cw_sync_prompt_values(d->nc); + + khm_cred_dispatch_process_message(d->nc); + + /* we won't know whether to abort or not until we get + feedback from the plugins, even if the command was + to cancel */ + { + HWND hw; + + hw = GetDlgItem(d->dlg_main, IDOK); + EnableWindow(hw, FALSE); + hw = GetDlgItem(d->dlg_main, IDCANCEL); + EnableWindow(hw, FALSE); + hw = GetDlgItem(d->dlg_bb, IDOK); + EnableWindow(hw, FALSE); + hw = GetDlgItem(d->dlg_bb, IDCANCEL); + EnableWindow(hw, FALSE); + } + return FALSE; + + case IDC_NC_OPTIONS: + /* the Options button in the main window was clicked. we + respond by expanding the dialog. */ + PostMessage(hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_DIALOG_EXPAND), 0); + return FALSE; + + case IDC_NC_CREDTEXT: /* credtext link activated */ + { + khui_htwnd_link * l; + wchar_t sid[KHUI_MAXCCH_HTLINK_FIELD]; + wchar_t sparam[KHUI_MAXCCH_HTLINK_FIELD]; + wchar_t * colon; + + l = (khui_htwnd_link *) lParam; + + /* do we have a valid link? */ + if(l->id == NULL || l->id_len >= ARRAYLENGTH(sid)) + return TRUE; /* nope */ + + StringCchCopyN(sid, ARRAYLENGTH(sid), l->id, l->id_len); + sid[l->id_len] = L'\0'; /* just make sure */ + + if(l->param != NULL && + l->param_len < ARRAYLENGTH(sparam) && + l->param_len > 0) { + + wcsncpy(sparam, l->param, l->param_len); + sparam[l->param_len] = L'\0'; + + } else { + sparam[0] = L'\0'; + } + + /* If the ID is of the form '<credtype>:<link_tag>' + and <credtype> is a valid name of a credentials + type that is participating in the credentials + acquisition process, then we forward the message to + the panel that is providing the UI for that cred + type. We also switch to that panel first. */ + + colon = wcschr(sid, L':'); + if (colon != NULL) { + khm_int32 credtype; + khui_new_creds_by_type * t; + + *colon = L'\0'; + if (KHM_SUCCEEDED(kcdb_credtype_get_id(sid, &credtype)) && + KHM_SUCCEEDED(khui_cw_find_type(d->nc, credtype, &t))){ + *colon = L':'; + + if (t->ordinal != d->ctab) + PostMessage(hwnd, + KHUI_WM_NC_NOTIFY, + MAKEWPARAM(t->ordinal, + WMNC_DIALOG_SWITCH_PANEL), + 0); + + return SendMessage(t->hwnd_panel, + KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_CREDTEXT_LINK), + lParam); + } + } + + /* if it was for us, then we need to process the message */ + if(!wcsicmp(sid, CTLINKID_SWITCH_PANEL)) { + khm_int32 credtype; + khui_new_creds_by_type * t; + + if (KHM_SUCCEEDED(kcdb_credtype_get_id(sparam, + &credtype)) && + KHM_SUCCEEDED(khui_cw_find_type(d->nc, + credtype, &t))) { + if (t->ordinal != d->ctab) + PostMessage(hwnd, + KHUI_WM_NC_NOTIFY, + MAKEWPARAM(t->ordinal, + WMNC_DIALOG_SWITCH_PANEL), + 0); + } + } + } + return FALSE; + + default: + /* if one of the tab strip buttons were pressed, then + we should switch to that panel */ + id = LOWORD(wParam); + if(id >= NC_TS_CTRL_ID_MIN && id <= NC_TS_CTRL_ID_MAX) { + id -= NC_TS_CTRL_ID_MIN; + PostMessage(hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(id, WMNC_DIALOG_SWITCH_PANEL),0); + return FALSE; + } + } + break; + } + + return TRUE; +} + +static LRESULT nc_handle_wm_moving(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + khui_nc_wnd_data * d; + + d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM); + + nc_notify_types(d->nc, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_DIALOG_MOVE), 0); + + return FALSE; +} + +static LRESULT nc_handle_wm_nc_notify(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + khui_nc_wnd_data * d; + RECT r; + int width, height; + khm_size id; + + d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM); + + switch(HIWORD(wParam)) { + + case WMNC_DIALOG_SWITCH_PANEL: + id = LOWORD(wParam); + if(id >= 0 && id <= d->nc->n_types) { + /* one of the tab buttons were pressed */ + if(d->ctab == id) { + return TRUE; /* nothign to do */ + } + + if(d->ctab == 0) { + ShowWindow(d->dlg_main, SW_HIDE); + SendMessage(d->hwnd_tc_main, + BM_SETCHECK, BST_UNCHECKED, 0); + } else { + ShowWindow(d->nc->types[d->ctab - 1]->hwnd_panel, SW_HIDE); + SendMessage(d->nc->types[d->ctab - 1]->hwnd_tc, + BM_SETCHECK, BST_UNCHECKED, 0); + } + + d->ctab = id; + + if(d->ctab == 0) { + ShowWindow(d->dlg_main, SW_SHOW); + SendMessage(d->hwnd_tc_main, + BM_SETCHECK, BST_CHECKED, 0); + } else { + ShowWindow(d->nc->types[id - 1]->hwnd_panel, SW_SHOW); + SendMessage(d->nc->types[id - 1]->hwnd_tc, + BM_SETCHECK, BST_CHECKED, 0); + } + } + + if(d->nc->mode == KHUI_NC_MODE_EXPANDED) + return TRUE; + /*else*/ + /* fallthrough */ + + case WMNC_DIALOG_EXPAND: + /* we are expanding the dialog box */ + + /* nothing to do? */ + if (d->nc->mode == KHUI_NC_MODE_EXPANDED) + break; + + d->nc->mode = KHUI_NC_MODE_EXPANDED; + + r.top = 0; + r.left = 0; + r.right = NCDLG_WIDTH + NCDLG_BBAR_WIDTH; + r.bottom = NCDLG_HEIGHT + NCDLG_TAB_HEIGHT; + + MapDialogRect(d->dlg_main, &r); + + width = r.right - r.left; + height = r.bottom - r.top; + + /* adjust width and height to accomodate NC area */ + { + RECT wr,cr; + + GetWindowRect(hwnd, &wr); + GetClientRect(hwnd, &cr); + + /* the non-client and client areas have already been + calculated at this point. We just use the difference + to adjust the width and height */ + width += (wr.right - wr.left) - (cr.right - cr.left); + height += (wr.bottom - wr.top) - (cr.bottom - cr.top); + } + + SendMessage(d->dlg_main, + KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0,WMNC_DIALOG_EXPAND), + 0); + + SetWindowPos(hwnd, + NULL, + 0, 0, + width, height, + SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER | + SWP_NOZORDER); + + ShowWindow(d->dlg_bb, SW_SHOW); + ShowWindow(d->dlg_ts, SW_SHOW); + break; + + case WMNC_DIALOG_SETUP: + if(d->nc->n_types > 0) { + khm_size i; + for(i=0; i < d->nc->n_types;i++) { + + if (d->nc->types[i]->dlg_proc == NULL) { + d->nc->types[i]->hwnd_panel = NULL; + } else { + /* Create the dialog panel */ + d->nc->types[i]->hwnd_panel = + CreateDialogParam(d->nc->types[i]->h_module, + d->nc->types[i]->dlg_template, + d->nc->hwnd, + d->nc->types[i]->dlg_proc, + (LPARAM) d->nc); + +#ifdef DEBUG + assert(d->nc->types[i]->hwnd_panel); +#endif + } + } + } + break; + + case WMNC_DIALOG_ACTIVATE: + { + int x,y,width,height; + RECT r; + int id; + wchar_t wbuf[256]; + HFONT hf; + + /* now we create all the tab strip controls */ + r.left = 0; + r.top = 0; + r.right = NCDLG_TAB_WIDTH; + r.bottom = NCDLG_TAB_HEIGHT; + MapDialogRect(d->dlg_main, &r); + + width = r.right - r.left; + height = r.bottom - r.top; + + x = 0; + y = 0; + + id = NC_TS_CTRL_ID_MIN; + + khui_cw_lock_nc(d->nc); + + /* first, the control for the main panel */ + LoadString(khm_hInstance, IDS_NC_IDENTITY, + wbuf, ARRAYLENGTH(wbuf)); + + d->hwnd_tc_main = + CreateWindow(L"BUTTON", + wbuf, + WS_VISIBLE | WS_CHILD | WS_TABSTOP | + BS_PUSHLIKE | BS_CHECKBOX | BS_TEXT, + x,y,width,height, + d->dlg_ts, + (HMENU)(INT_PTR) id, + khm_hInstance, + NULL); + + hf = (HFONT) SendMessage(d->dlg_main, WM_GETFONT, 0, 0); + SendMessage(d->hwnd_tc_main, WM_SETFONT, (WPARAM) hf, 0); + SendMessage(d->hwnd_tc_main, BM_SETCHECK, BST_CHECKED, 0); + + id++; + x += width; + + if(d->nc->n_types > 0) { + khm_size i; + /* we should sort the tabs first */ + qsort(d->nc->types, + d->nc->n_types, + sizeof(*(d->nc->types)), + nc_tab_sort_func); + + for(i=0; i < d->nc->n_types;i++) { + wchar_t * name; + + d->nc->types[i]->ordinal = i + 1; + + if(d->nc->types[i]->name) + name = d->nc->types[i]->name; + else { + khm_size cbsize; + + if(kcdb_credtype_describe + (d->nc->types[i]->type, + NULL, + &cbsize, + KCDB_TS_SHORT) == KHM_ERROR_TOO_LONG) { + + name = malloc(cbsize); + kcdb_credtype_describe(d->nc->types[i]->type, + name, + &cbsize, + KCDB_TS_SHORT); + } else { +#ifdef DEBUG + assert(FALSE); +#else + continue; +#endif + } + } + + d->nc->types[i]->hwnd_tc = + CreateWindow(L"BUTTON", + name, + WS_VISIBLE | WS_CHILD | WS_TABSTOP | + BS_PUSHLIKE | BS_CHECKBOX | BS_TEXT | + ((d->nc->types[i]->hwnd_panel == NULL)? + WS_DISABLED : 0), + x,y,width,height, + d->dlg_ts, + (HMENU)(INT_PTR) id, + khm_hInstance, + NULL); + + SendMessage(d->nc->types[i]->hwnd_tc, WM_SETFONT, + (WPARAM)hf, 0); + +#if 0 + if(d->nc->types[i]->flags & KHUI_NCT_FLAG_DISABLED) + SendMessage(d->nc->types[i]->hwnd_tc, + BM_SETIMAGE, + IMAGE_ICON, + LoadIcon(khm_hInstance, MAKEINTRESOURCE(IDI_DISABLED))); + else + SendMessage(d->nc->types[i]->hwnd_tc, + BM_SETIMAGE, + IMAGE_ICON, + LoadIcon(khm_hInstance, MAKEINTRESOURCE(IDI_ENABLED))); +#endif + + id++; + x += width; + + if(!(d->nc->types[i]->name)) + free(name); + + /* Now set the position of the type panel */ + ShowWindow(d->nc->types[i]->hwnd_panel, SW_HIDE); + SetWindowPos(d->nc->types[i]->hwnd_panel, + NULL, + d->r_main.left, + d->r_main.top, + d->r_main.right - d->r_main.left, + d->r_main.bottom - d->r_main.top, + SWP_DEFERERASE | SWP_NOACTIVATE | + SWP_NOOWNERZORDER | SWP_NOREDRAW | + SWP_NOZORDER); + + } + } + + khui_cw_unlock_nc(d->nc); + + nc_update_credtext(d); + + ShowWindow(hwnd, SW_SHOW); + SetFocus(hwnd); + + if (d->nc->n_identities == 0) + break; + /* else */ + /* fallthrough */ + } + + case WMNC_IDENTITY_CHANGE: + { + BOOL okEnable = FALSE; + + nc_notify_types(d->nc, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_IDENTITY_CHANGE), 0); + nc_update_credtext(d); + } + break; + + case WMNC_TYPE_STATE: + /* fallthrough */ + case WMNC_UPDATE_CREDTEXT: + nc_update_credtext(d); + break; + + case WMNC_CLEAR_PROMPTS: + { + khm_size i; + + khui_cw_lock_nc(d->nc); + + if(d->hwnd_banner != NULL) { + DestroyWindow(d->hwnd_banner); + d->hwnd_banner = NULL; + } + + if(d->hwnd_name != NULL) { + DestroyWindow(d->hwnd_name); + d->hwnd_name = NULL; + } + + for(i=0;i<d->nc->n_prompts;i++) { + if(!(d->nc->prompts[i]->flags & + KHUI_NCPROMPT_FLAG_STOCK)) { + if(d->nc->prompts[i]->hwnd_static != NULL) + DestroyWindow(d->nc->prompts[i]->hwnd_static); + + if(d->nc->prompts[i]->hwnd_edit != NULL) + DestroyWindow(d->nc->prompts[i]->hwnd_edit); + } + + d->nc->prompts[i]->hwnd_static = NULL; + d->nc->prompts[i]->hwnd_edit = NULL; + } + + khui_cw_unlock_nc(d->nc); + + d->r_credtext.top = d->r_idspec.bottom; + + nc_position_credtext(d); + } + break; + + case WMNC_SET_PROMPTS: + { + khm_size i; + int y; + HWND hw, hw_prev; + HFONT hf, hfold; + HDC hdc; + + /* we assume that WMNC_CLEAR_PROMPTS has already been + received */ + + khui_cw_lock_nc(d->nc); + +#if 0 + /* special case, we have one prompt and it is a password + prompt. very common */ + if(d->nc->n_prompts == 1 && + d->nc->prompts[0]->type == KHUI_NCPROMPT_TYPE_PASSWORD) { + + hw = GetDlgItem(d->dlg_main, IDC_NC_PASSWORD); + EnableWindow(hw, TRUE); + + d->nc->prompts[0]->flags |= KHUI_NCPROMPT_FLAG_STOCK; + d->nc->prompts[0]->hwnd_edit = hw; + d->nc->prompts[0]->hwnd_static = NULL; /* don't care */ + + khui_cw_unlock_nc(d->nc); + break; + } +#endif + /* for everything else */ + + /* hide the stock password controls */ +#if 0 + /* TAGREMOVE */ + hw = GetDlgItem(d->dlg_main, IDC_NC_PASSWORD); + ShowWindow(hw, SW_HIDE); + hw = GetDlgItem(d->dlg_main, IDC_NC_PASSWORD_LABEL); + ShowWindow(hw, SW_HIDE); +#endif + + y = d->r_idspec.bottom; + + hf = (HFONT) SendMessage(d->dlg_main, WM_GETFONT, 0, 0); + + if (d->nc->pname != NULL) { + hw = + CreateWindowEx + (0, + L"STATIC", + d->nc->pname, + SS_SUNKEN | WS_CHILD, + d->r_area.left, y, + d->r_row.right, + d->r_n_label.bottom - d->r_n_label.top, + d->dlg_main, + NULL, + khm_hInstance, + NULL); + +#ifdef DEBUG + assert(hw); +#endif + d->hwnd_name = hw; + SendMessage(hw, WM_SETFONT, (WPARAM)hf, (LPARAM) TRUE); + ShowWindow(hw, SW_SHOW); + + y += d->r_n_label.bottom - d->r_n_label.top; + } + + if (d->nc->banner != NULL) { + hw = + CreateWindowEx + (0, + L"STATIC", + d->nc->banner, + WS_CHILD, + d->r_area.left, y, + d->r_row.right, d->r_row.bottom, + d->dlg_main, + NULL, + khm_hInstance, + NULL); +#ifdef DEBUG + assert(hw); +#endif + d->hwnd_banner = hw; + SendMessage(hw, WM_SETFONT, (WPARAM)hf, (LPARAM)TRUE); + ShowWindow(hw, SW_SHOW); + y += d->r_row.bottom; + } + + hw_prev = d->hwnd_last_idspec; + + hdc = GetWindowDC(d->dlg_main); + hfold = SelectObject(hdc,hf); + + for(i=0; i<d->nc->n_prompts; i++) { + RECT pr, er; + SIZE s; + int dy; + + if(d->nc->prompts[i]->prompt != NULL) { + GetTextExtentPoint32(hdc, + d->nc->prompts[i]->prompt, + (int) wcslen(d->nc->prompts[i]->prompt), + &s); + if(s.cx < d->r_n_label.right - d->r_n_label.left) { + CopyRect(&pr, &d->r_n_label); + CopyRect(&er, &d->r_n_input); + dy = d->r_row.bottom; + } else if(s.cx < + d->r_e_label.right - d->r_e_label.left) { + CopyRect(&pr, &d->r_e_label); + CopyRect(&er, &d->r_e_input); + dy = d->r_row.bottom; + } else { + /* oops. the prompt doesn't fit in our + controls. we need to use up two lines */ + pr.left = 0; + pr.right = d->r_row.right; + pr.top = 0; + pr.bottom = d->r_n_label.bottom - + d->r_n_label.top; + CopyRect(&er, &d->r_n_input); + OffsetRect(&er, 0, pr.bottom); + dy = er.bottom + (d->r_row.bottom - + d->r_n_input.bottom); + } + } else { + SetRectEmpty(&pr); + CopyRect(&er, &d->r_n_input); + dy = d->r_row.bottom; + } + + if(IsRectEmpty(&pr)) { + d->nc->prompts[i]->hwnd_static = NULL; + } else { + OffsetRect(&pr, d->r_area.left, y); + + hw = CreateWindowEx + (0, + L"STATIC", + d->nc->prompts[i]->prompt, + WS_CHILD, + pr.left, pr.top, + pr.right - pr.left, pr.bottom - pr.top, + d->dlg_main, + NULL, + khm_hInstance, + NULL); +#ifdef DEBUG + assert(hw); +#endif + + SendMessage(hw, WM_SETFONT, + (WPARAM) hf, (LPARAM) TRUE); + + SetWindowPos(hw, hw_prev, + 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | + SWP_NOOWNERZORDER | SWP_NOSIZE | + SWP_SHOWWINDOW); + + d->nc->prompts[i]->hwnd_static = hw; + hw_prev = hw; + } + + OffsetRect(&er, d->r_area.left, y); + + hw = CreateWindowEx + (0, + L"EDIT", + (d->nc->prompts[i]->def ? + d->nc->prompts[i]->def : L""), + WS_CHILD | WS_TABSTOP | + WS_BORDER | + ((d->nc->prompts[i]->flags & + KHUI_NCPROMPT_FLAG_HIDDEN)? ES_PASSWORD:0), + er.left, er.top, + er.right - er.left, er.bottom - er.top, + d->dlg_main, + NULL, + khm_hInstance, + NULL); + +#ifdef DEBUG + assert(hw); +#endif + + SendMessage(hw, WM_SETFONT, + (WPARAM) hf, (LPARAM) TRUE); + + SetWindowPos(hw, hw_prev, + 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | + SWP_NOOWNERZORDER | SWP_NOSIZE | + SWP_SHOWWINDOW); + + d->nc->prompts[i]->hwnd_edit = hw; + + hw_prev = hw; + + y += dy; + } + + SelectObject(hdc, hfold); + ReleaseDC(d->dlg_main, hdc); + + khui_cw_unlock_nc(d->nc); + + d->r_credtext.top = y; + + nc_position_credtext(d); + } + break; + + case WMNC_DIALOG_PROCESS_COMPLETE: + { + khui_new_creds * nc; + + nc = d->nc; + + /* reset state */ + nc->result = KHUI_NC_RESULT_CANCEL; + + if(nc->response & KHUI_NC_RESPONSE_NOEXIT) { + HWND hw; + + hw = GetDlgItem(d->dlg_main, IDOK); + EnableWindow(hw, TRUE); + hw = GetDlgItem(d->dlg_main, IDCANCEL); + EnableWindow(hw, TRUE); + hw = GetDlgItem(d->dlg_bb, IDOK); + EnableWindow(hw, TRUE); + hw = GetDlgItem(d->dlg_bb, IDCANCEL); + EnableWindow(hw, TRUE); + + return TRUE; + } + + DestroyWindow(hwnd); + + kmq_post_message(KMSG_CRED, KMSG_CRED_END, 0, (void *) nc); + } + break; + + /* MUST be called with SendMessage */ + case WMNC_ADD_CONTROL_ROW: + { + khui_control_row * row; + + row = (khui_control_row *) lParam; + +#ifdef DEBUG + assert(row->label); + assert(row->input); +#endif + + nc_add_control_row(d, row->label, row->input, row->size); + } + break; + } /* switch(HIWORD(wParam)) */ + + return TRUE; +} + + +static LRESULT CALLBACK nc_window_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + switch(uMsg) { + case WM_CREATE: + return nc_handle_wm_create(hwnd, uMsg, wParam, lParam); + + case WM_DESTROY: + return nc_handle_wm_destroy(hwnd, uMsg, wParam, lParam); + + case WM_COMMAND: + return nc_handle_wm_command(hwnd, uMsg, wParam, lParam); + + case WM_MOVE: + case WM_MOVING: + return nc_handle_wm_moving(hwnd, uMsg, wParam, lParam); + + case KHUI_WM_NC_NOTIFY: + return nc_handle_wm_nc_notify(hwnd, uMsg, wParam, lParam); + } + + /* Note that this is technically a dialog box */ + return DefDlgProc(hwnd, uMsg, wParam, lParam); +} + +void khm_register_newcredwnd_class(void) +{ + WNDCLASSEX wcx; + + wcx.cbSize = sizeof(wcx); + wcx.style = CS_DBLCLKS | CS_OWNDC; + wcx.lpfnWndProc = nc_window_proc; + wcx.cbClsExtra = 0; + wcx.cbWndExtra = DLGWINDOWEXTRA + sizeof(LONG_PTR); + wcx.hInstance = khm_hInstance; + wcx.hIcon = LoadIcon(khm_hInstance, MAKEINTRESOURCE(IDI_MAIN_APP)); + wcx.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW); + wcx.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1); + wcx.lpszMenuName = NULL; + wcx.lpszClassName = KHUI_NEWCREDWND_CLASS; + wcx.hIconSm = NULL; + + khui_newcredwnd_cls = RegisterClassEx(&wcx); +} + +void khm_unregister_newcredwnd_class(void) +{ + UnregisterClass((LPWSTR) khui_newcredwnd_cls, khm_hInstance); +} + +HWND khm_create_newcredwnd(HWND parent, khui_new_creds * c) +{ + wchar_t wtitle[256]; + HWND hwnd; + + if (c->window_title == NULL) { + if (c->subtype == KMSG_CRED_PASSWORD) + LoadString(khm_hInstance, + IDS_WT_PASSWORD, + wtitle, + ARRAYLENGTH(wtitle)); + else + LoadString(khm_hInstance, + IDS_WT_NEW_CREDS, + wtitle, + ARRAYLENGTH(wtitle)); + } + + hwnd = CreateWindowEx(WS_EX_DLGMODALFRAME | WS_EX_CONTEXTHELP, + MAKEINTATOM(khui_newcredwnd_cls), + ((c->window_title)?c->window_title: wtitle), + WS_DLGFRAME | WS_POPUPWINDOW | WS_CLIPCHILDREN, + 0,0,400,400, /* bogus values. the window + is going to resize and + reposition itself + anyway */ + parent, + NULL, + khm_hInstance, + (LPVOID) c); + +#ifdef DEBUG + assert(hwnd != NULL); +#endif + + /* note that the window is not visible yet. That's because, at + this point we don't know what the panels are */ + + return hwnd; +} + +void khm_prep_newcredwnd(HWND hwnd) +{ + SendMessage(hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_DIALOG_SETUP), 0); +} + +void khm_show_newcredwnd(HWND hwnd) +{ + /* add all the panels in and prep UI */ + SendMessage(hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_DIALOG_ACTIVATE), 0); +} diff --git a/src/windows/identity/ui/newcredwnd.h b/src/windows/identity/ui/newcredwnd.h new file mode 100644 index 000000000..d0b676422 --- /dev/null +++ b/src/windows/identity/ui/newcredwnd.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_NEWCREDWND_H +#define __KHIMAIRA_NEWCREDWND_H + +#include<khuidefs.h> + +#define KHUI_NEWCREDWND_CLASS L"KhmNewCredWnd" + +typedef struct khui_nc_wnd_data_t { + khui_new_creds * nc; + + HWND dlg_main; /* main dialog */ + RECT r_main; + HWND dlg_bb; /* button bar */ + RECT r_bb; + HWND dlg_ts; /* tab strip */ + RECT r_ts; + + khm_size ctab; /* current tab */ + + HWND hwnd_tc_main; /* tab control button for main dialog */ + + HWND hwnd_banner; /* static control for banner */ + HWND hwnd_name; /* static control for name */ + + HWND hwnd_last_idspec; /* last identity specifier control */ + + /* metrics for custom prompts and identity specifiers */ + + RECT r_idspec; /* Area used by identity specifiers + (relative to client) */ + RECT r_row; /* Metrics for a control row + (top=0,left=0,right=width, + bottom=height) */ + RECT r_area; /* Area available for controls (relative + to client) */ + RECT r_n_label; /* coords of the static control (relative + to row) */ + RECT r_n_input; /* coords of the edit control (relative to + row) */ + RECT r_e_label; /* coords of the extended edit control + (relative to row) */ + RECT r_e_input; /* coords of the extended edit control + (relative to row) */ + RECT r_credtext; /* Area for credtext window (relative to + row) */ +} khui_nc_wnd_data; + +void khm_register_newcredwnd_class(void); +void khm_unregister_newcredwnd_class(void); +HWND khm_create_newcredwnd(HWND parent, khui_new_creds * c); +void khm_prep_newcredwnd(HWND hwnd); +void khm_show_newcredwnd(HWND hwnd); + +/* This is the first control ID that is created in the custom tabstrip + control buttons. Subsequent buttons get consecutive IDs starting + from this one. */ +#define NC_TS_CTRL_ID_MIN 8001 + +/* Maximum number of controls */ +#define NC_TS_MAX_CTRLS 8 + +/* Maximum control ID */ +#define NC_TS_CTRL_ID_MAX (NC_TS_CTRL_ID_MIN + NC_TS_MAX_CTRLS - 1) + +/* the first control ID that may be used by an identity provider */ +#define NC_IS_CTRL_ID_MIN 8016 + +/* the maximum number of controls that may be created by an identity + provider*/ +#define NC_IS_CTRL_MAX_CTRLS 8 + +/* the maximum control ID that may be used by an identity provider */ +#define NC_IS_CTRL_ID_MAX (NC_IS_CTRL_ID_MIN + NC_IS_MAX_CTRLS - 1) + +#endif diff --git a/src/windows/identity/ui/notifier.c b/src/windows/identity/ui/notifier.c new file mode 100644 index 000000000..0ebdbd4cb --- /dev/null +++ b/src/windows/identity/ui/notifier.c @@ -0,0 +1,1079 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#define OEMRESOURCE + +#include<khmapp.h> +#include<assert.h> + +#define KHUI_NOTIFIER_CLASS L"KhuiNotifierMsgWindowClass" +#define KHUI_ALERTER_CLASS L"KhuiAlerterWindowClass" + +#define KHUI_NOTIFIER_WINDOW L"KhuiNotifierMsgWindow" + +/* notifier message for notification icon */ +#define KHUI_WM_NOTIFIER WM_COMMAND + +/* window class registration atom for message only notifier window + class */ +ATOM atom_notifier = 0; + +/* window class registration atom for alert windows */ +ATOM atom_alerter = 0; + +/* notifier message window */ +HWND hwnd_notifier = NULL; + +BOOL notifier_ready = FALSE; + +khui_alert * current_alert = NULL; + + +/* forward dcls */ +static khm_int32 +alert_show(khui_alert * a); + +static khm_int32 +alert_show_minimized(khui_alert * a); + +static khm_int32 +alert_show_normal(khui_alert * a); + +/********************************************************************** + Notifier +*********************************************************************** + +The notifier is a message only window that listens for notifier +messages. This window will exist for the lifetime of the application +and will use alerter windows as needed to show application alerts. +*/ + +static LRESULT CALLBACK +notifier_wnd_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + kmq_message * m; + khm_int32 rv; + + if(uMsg == KMQ_WM_DISPATCH) { + kmq_wm_begin(lParam, &m); + rv = KHM_ERROR_SUCCESS; + + if(m->type == KMSG_ALERT) { + /* handle notifier messages */ + switch(m->subtype) { + case KMSG_ALERT_SHOW: + rv = alert_show((khui_alert *) m->vparam); + khui_alert_release((khui_alert *) m->vparam); + break; + } + } else if (m->type == KMSG_CRED && + m->subtype == KMSG_CRED_ROOTDELTA) { + KillTimer(hwnd, KHUI_REFRESH_TIMER_ID); + SetTimer(hwnd, KHUI_REFRESH_TIMER_ID, + KHUI_REFRESH_TIMEOUT, + NULL); + } + + return kmq_wm_end(m, rv); + } else if (uMsg == KHUI_WM_NOTIFIER) { + /* Handle events generated from the notification icon */ + + /* wParam is the identifier of the notify icon, but we only + have one. */ + switch(lParam) { + case WM_CONTEXTMENU: + { + POINT pt; + int menu_id; + + GetCursorPos(&pt); + + if (khm_is_main_window_visible()) + menu_id = KHUI_MENU_ICO_CTX_NORMAL; + else + menu_id = KHUI_MENU_ICO_CTX_MIN; + + SetForegroundWindow(khm_hwnd_main); + + khm_menu_show_panel(menu_id, pt.x, pt.y); + + PostMessage(khm_hwnd_main, WM_NULL, 0, 0); + } + break; + + case WM_LBUTTONDOWN: + /* we actually wait for the WM_LBUTTONUP before doing + anything */ + break; + + case WM_LBUTTONUP: + /* fall through */ + + case NIN_SELECT: + case NIN_KEYSELECT: + khm_show_main_window(); + break; + + case NIN_BALLOONUSERCLICK: + if (current_alert) { + if ((current_alert->flags & KHUI_ALERT_FLAG_DEFACTION) && + current_alert->n_alert_commands > 0) { + PostMessage(khm_hwnd_main, WM_COMMAND, + MAKEWPARAM(current_alert->alert_commands[0], 0), + 0); + } else if (current_alert->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW) { + khm_show_main_window(); + alert_show_normal(current_alert); + } + } + /* fallthrough */ + case NIN_BALLOONTIMEOUT: + khui_notify_icon_change(KHERR_NONE); + if (current_alert) { + khui_alert_release(current_alert); + current_alert = NULL; + } + break; + } + } else if (uMsg == WM_TIMER) { + if (wParam == KHUI_TRIGGER_TIMER_ID) { + KillTimer(hwnd, KHUI_TRIGGER_TIMER_ID); + khm_timer_fire(hwnd); + } else if (wParam == KHUI_REFRESH_TIMER_ID) { + KillTimer(hwnd, KHUI_REFRESH_TIMER_ID); + khm_timer_refresh(hwnd); + } + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +ATOM +khui_register_notifier_wnd_class(void) +{ + WNDCLASSEX wcx; + + ZeroMemory(&wcx, sizeof(wcx)); + + wcx.cbSize = sizeof(wcx); + wcx.style = 0; + wcx.lpfnWndProc = notifier_wnd_proc; + wcx.cbClsExtra = 0; + wcx.cbWndExtra = 0; + wcx.hInstance = khm_hInstance; + wcx.hIcon = NULL; + wcx.hCursor = NULL; + wcx.hbrBackground = NULL; + wcx.lpszMenuName = NULL; + wcx.lpszClassName = KHUI_NOTIFIER_CLASS; + wcx.hIconSm = NULL; + + atom_notifier = RegisterClassEx(&wcx); + + return atom_notifier; +} + +/********************************************************************* + Alerter +**********************************************************************/ + +typedef struct tag_alerter_wnd_data { + khui_alert * alert; + + HWND hwnd; + HFONT hfont; + + BOOL metrics_done; + + HWND hwnd_buttons[KHUI_MAX_ALERT_COMMANDS]; + + /* various metrics */ + + /* calculated during WM_CREATE and adjusted during WM_PAINT */ + int dy_message; + int dy_suggestion; + + /* calculated during WM_CREATE */ + int dx_button; + int dy_button; + int dx_button_incr; + int dx_margin; + int dy_margin; + int dy_bb; + int x_message; + int dx_message; + int dx_icon; + int dy_icon; + int dx_suggest_pad; + + /* calculated during WM_CREATE and adjusted during WM_PAINT */ + int dx_client; + int dy_client; + + /* calculated during WM_PAINT */ + int y_message; + int y_suggestion; + + LDCL(struct tag_alerter_wnd_data); +} alerter_wnd_data; + +alerter_wnd_data * khui_alerts = NULL; + +#define NTF_PARAM DWLP_USER + +/* dialog sizes in base dialog units */ + +#define NTF_MARGIN 5 +#define NTF_WIDTH 200 + +#define NTF_BB_HEIGHT 15 + +#define NTF_ICON_X NTF_MARGIN +#define NTF_ICON_WIDTH 20 +#define NTF_ICON_HEIGHT 20 + +#define NTF_MSG_X (NTF_ICON_X + NTF_ICON_WIDTH + NTF_MARGIN) +#define NTF_MSG_WIDTH ((NTF_WIDTH - NTF_MARGIN) - NTF_MSG_X) +#define NTF_MSG_HEIGHT 15 + +#define NTF_SUG_X NTF_MSG_X +#define NTF_SUG_WIDTH NTF_MSG_WIDTH +#define NTF_SUG_HEIGHT NTF_MSG_HEIGHT +#define NTF_SUG_PAD 2 + +#define NTF_BUTTON_X NTF_MSG_X + +#define NTF_BUTTON_WIDTH ((NTF_MSG_WIDTH - 3*NTF_MARGIN) / 4) +#define NTF_BUTTON_XINCR (NTF_BUTTON_WIDTH + NTF_MARGIN) +#define NTF_BUTTON_HEIGHT (NTF_BB_HEIGHT - NTF_MARGIN) + +#define NTF_TIMEOUT 20000 + +static khm_int32 +alert_show_minimized(khui_alert * a) { + wchar_t tbuf[64]; + wchar_t mbuf[256]; + + if (a->message == NULL) + return KHM_ERROR_SUCCESS; + + if (a->title == NULL) { + LoadString(khm_hInstance, IDS_ALERT_DEFAULT, + tbuf, ARRAYLENGTH(tbuf)); + } else { + StringCbCopy(tbuf, sizeof(tbuf), a->title); + } + + if (FAILED(StringCbCopy(mbuf, sizeof(mbuf), a->message)) || + (!(a->flags & KHUI_ALERT_FLAG_DEFACTION) && + (a->n_alert_commands > 0 || + a->suggestion || + (a->flags & KHUI_ALERT_FLAG_VALID_ERROR)))) { + /* if mbuf wasn't big enough, this should have copied a + truncated version of it */ + size_t cch_m, cch_p; + wchar_t postfix[256]; + + cch_p = LoadString(khm_hInstance, IDS_ALERT_MOREINFO, postfix, + ARRAYLENGTH(postfix)); + cch_p++; /* account for NULL */ + + StringCchLength(mbuf, ARRAYLENGTH(mbuf), &cch_m); + cch_m = min(cch_m, ARRAYLENGTH(mbuf) - cch_p); + + StringCchCopy(mbuf + cch_m, ARRAYLENGTH(mbuf) - cch_m, + postfix); + + a->flags |= KHUI_ALERT_FLAG_REQUEST_WINDOW; + } + + a->flags |= KHUI_ALERT_FLAG_DISPLAY_BALLOON; + + current_alert = a; + khui_alert_hold(a); + + khui_notify_icon_balloon(a->severity, + tbuf, + mbuf, + NTF_TIMEOUT); + + return KHM_ERROR_SUCCESS; +} + +static khm_int32 +alert_show_normal(khui_alert * a) { + HWND hwa; + wchar_t buf[256]; + wchar_t * title; + + if(a->title == NULL) { + LoadString(khm_hInstance, IDS_ALERT_DEFAULT, + buf, ARRAYLENGTH(buf)); + title = buf; + } else + title = a->title; + + /* if we don't have any commands, we just add a "close" button */ + if(a->n_alert_commands == 0) { + khui_alert_add_command(a, KHUI_PACTION_CLOSE); + } + + /* we don't need to keep track of the window handle + because the window procedure adds it to the dialog + list automatically */ + + hwa = + CreateWindowEx(WS_EX_DLGMODALFRAME | WS_EX_CONTEXTHELP, + MAKEINTATOM(atom_alerter), + title, + WS_DLGFRAME | WS_POPUPWINDOW | WS_CLIPCHILDREN | + WS_VISIBLE, + 0, 0, 300, 300, // bogus values + khm_hwnd_main, + (HMENU) NULL, + khm_hInstance, + (LPVOID) a); + + return KHM_ERROR_SUCCESS; +} + +static khm_int32 +alert_show(khui_alert * a) { + /* the window has already been shown */ + if((a->flags & KHUI_ALERT_FLAG_DISPLAY_WINDOW) || + ((a->flags & KHUI_ALERT_FLAG_DISPLAY_BALLOON) && + !(a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW))) + return KHM_ERROR_SUCCESS; + + if(a->err_context != NULL || + a->err_event != NULL) { + khui_alert_lock(a); + a->flags |= KHUI_ALERT_FLAG_VALID_ERROR; + khui_alert_unlock(a); + } + + /* depending on the state of the main window, we + need to either show a window or a balloon */ + if(khm_is_main_window_active() && + !(a->flags & KHUI_ALERT_FLAG_REQUEST_BALLOON)) + return alert_show_normal(a); + else + return alert_show_minimized(a); +} + +/* the alerter window is actually a dialog */ +static LRESULT CALLBACK +alerter_wnd_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + switch(uMsg) { + case WM_CREATE: + { + LONG dlgb; + HWND hwnd_parent; + RECT r_parent; + POINT pos; + SIZE s; + LPCREATESTRUCT lpcs; + khui_alert * a; + alerter_wnd_data * d; + + lpcs = (LPCREATESTRUCT) lParam; + a = (khui_alert *) lpcs->lpCreateParams; + khui_alert_hold(a); + + d = malloc(sizeof(*d)); + ZeroMemory(d, sizeof(*d)); + + d->alert = a; + d->hwnd = hwnd; + + khui_alert_lock(a); + + a->flags |= KHUI_ALERT_FLAG_DISPLAY_WINDOW; + LPUSH(&khui_alerts, d); + +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd, NTF_PARAM, (LONG_PTR) d); +#pragma warning(pop) + + khm_add_dialog(hwnd); + khm_enter_modal(hwnd); + + /* now figure out the size and position of the window */ + + hwnd_parent = GetWindow(hwnd, GW_OWNER); + GetWindowRect(hwnd_parent, &r_parent); + + dlgb = GetDialogBaseUnits(); + +#define DLG2SCNX(x) MulDiv((x), LOWORD(dlgb), 4) +#define DLG2SCNY(y) MulDiv((y), HIWORD(dlgb), 8) + + d->dx_margin = DLG2SCNX(NTF_MARGIN); + d->dy_margin = DLG2SCNY(NTF_MARGIN); + + d->x_message = DLG2SCNX(NTF_MSG_X); + d->dx_message = DLG2SCNX(NTF_MSG_WIDTH); + + if (a->message) { + d->dy_message = DLG2SCNY(NTF_MSG_HEIGHT); + } + + if (a->suggestion) { + d->dy_suggestion = DLG2SCNY(NTF_SUG_HEIGHT); + d->dx_suggest_pad = DLG2SCNX(NTF_SUG_PAD); + } + + d->dy_bb = DLG2SCNY(NTF_BB_HEIGHT); + d->dx_button = DLG2SCNX(NTF_BUTTON_WIDTH); + d->dy_button = DLG2SCNY(NTF_BUTTON_HEIGHT); + d->dx_button_incr = DLG2SCNX(NTF_BUTTON_XINCR); + + d->dx_icon = DLG2SCNX(NTF_ICON_WIDTH); + d->dy_icon = DLG2SCNY(NTF_ICON_HEIGHT); + + d->dx_client = DLG2SCNX(NTF_WIDTH); + d->dy_client = max(d->dy_icon, + d->dy_message + + ((d->dy_suggestion > 0)? + (d->dy_suggestion + d->dy_margin): + 0)) + + d->dy_margin * 3 + d->dy_bb; + + /* adjust for client rect */ + s.cx = d->dx_client; + s.cy = d->dy_client; + + { + RECT c_r; + RECT w_r; + + GetWindowRect(hwnd, &w_r); + GetClientRect(hwnd, &c_r); + + s.cx += (w_r.right - w_r.left) - (c_r.right - c_r.left); + s.cy += (w_r.bottom - w_r.top) - (c_r.bottom - c_r.top); + } + + pos.x = (r_parent.left + r_parent.right - s.cx) / 2; + pos.y = (r_parent.top + r_parent.bottom - s.cy) / 2; + + SetWindowPos(hwnd, + HWND_TOP, + pos.x, pos.y, + s.cx, s.cy, + SWP_SHOWWINDOW); + + { + LOGFONT lf; + HDC hdc_dt; + + hdc_dt = GetDC(NULL); + + lf.lfHeight = -MulDiv(8, + GetDeviceCaps(hdc_dt, LOGPIXELSY), + 72); + lf.lfWidth = 0; + lf.lfEscapement = 0; + lf.lfOrientation = 0; + lf.lfWeight = FW_NORMAL; + lf.lfItalic = FALSE; + lf.lfUnderline = FALSE; + lf.lfStrikeOut = FALSE; + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfOutPrecision = OUT_DEFAULT_PRECIS; + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf.lfQuality = DEFAULT_QUALITY; + lf.lfPitchAndFamily = DEFAULT_PITCH; + + LoadString(khm_hInstance, IDS_DEFAULT_FONT, + lf.lfFaceName, ARRAYLENGTH(lf.lfFaceName)); + + d->hfont = CreateFontIndirect(&lf); + + ReleaseDC(NULL, hdc_dt); + } + + /* create dialog controls now */ + { + int x,y; + int width, height; + int i; + + x = d->x_message; + y = d->dy_client - d->dy_bb; + width = d->dx_button; + height = d->dy_button; + + for(i=0; i<a->n_alert_commands; i++) { + wchar_t caption[256]; + khui_action * action; + HWND hw_button; + + if(a->alert_commands[i] == 0) + continue; + + action = khui_find_action(a->alert_commands[i]); + if(action == NULL) + continue; + + LoadString(khm_hInstance, action->is_caption, + caption, ARRAYLENGTH(caption)); + + hw_button = + CreateWindowEx(0, + L"BUTTON", + caption, + WS_VISIBLE | WS_CHILD, + x,y,width,height, + hwnd, + (HMENU)(INT_PTR) (action->cmd), + khm_hInstance, + NULL); + + SendMessage(hw_button, WM_SETFONT, + (WPARAM) d->hfont, MAKELPARAM(TRUE, 0)); + + d->hwnd_buttons[i] = hw_button; + + x += d->dx_button_incr; + } + } + + khui_notify_icon_change(a->severity); + + khui_alert_unlock(a); + + d->metrics_done = FALSE; + + return TRUE; + } + break; /* not reached */ + + case WM_DESTROY: + { + alerter_wnd_data * d; + + /* khm_leave_modal() could be here, but instead it is in + the WM_COMMAND handler. This is because the modal loop + has to be exited before DestroyWindow() is issued. */ + //khm_leave_modal(); + khm_del_dialog(hwnd); + + d = (alerter_wnd_data *)(LONG_PTR) + GetWindowLongPtr(hwnd, NTF_PARAM); + + LDELETE(&khui_alerts, d); + + khui_alert_lock(d->alert); + d->alert->flags &= ~KHUI_ALERT_FLAG_DISPLAY_WINDOW; + khui_alert_unlock(d->alert); + + khui_alert_release(d->alert); + + DeleteObject(d->hfont); + + free(d); + + khui_notify_icon_change(KHERR_NONE); + + return TRUE; + } + break; + + case WM_PAINT: + { + RECT r_update; + PAINTSTRUCT ps; + HDC hdc; + LONG dlgb; + alerter_wnd_data * d; + HFONT hf_old; + BOOL need_resize = FALSE; + + if(!GetUpdateRect(hwnd, &r_update, TRUE)) + return FALSE; + + d = (alerter_wnd_data *)(LONG_PTR) + GetWindowLongPtr(hwnd, NTF_PARAM); + + dlgb = GetDialogBaseUnits(); + + hdc = BeginPaint(hwnd, &ps); + + hf_old = SelectFont(hdc, d->hfont); + + khui_alert_lock(d->alert); + + // draw the severity icon + { + HICON hicon; + int x,y; + int iid; + + /* GOINGHERE! If the metrics for the window haven't + been calculated yet, then calculate them. If the + hight needs to be expanded, then do that and wait + for the next repaint cycle. Also move the button + controls down. */ + x = d->dx_margin; + y = d->dy_margin; + + if(d->alert->severity == KHERR_ERROR) + iid = OIC_HAND; + else if(d->alert->severity == KHERR_WARNING) + iid = OIC_BANG; + else + iid = OIC_NOTE; + + hicon = LoadImage(NULL, + MAKEINTRESOURCE(iid), + IMAGE_ICON, + SM_CXICON, SM_CYICON, + LR_SHARED); + + DrawIcon(hdc, x, y, hicon); + } + + // draw the message + if(d->alert->message) { + RECT r; + int width; + int height; + size_t cch; + + r.left = d->x_message; + r.top = d->dy_margin; + width = d->dx_message; + r.right = r.left + width; + height = d->dy_message; + r.bottom = r.top + height; + + StringCchLength(d->alert->message, + KHUI_MAXCCH_MESSAGE, &cch); + + height = DrawText(hdc, + d->alert->message, + (int) cch, + &r, + DT_WORDBREAK | + DT_CALCRECT); + + if (height > d->dy_message) { + d->dy_message = height; + need_resize = TRUE; + } else { + DrawText(hdc, + d->alert->message, + (int) cch, + &r, + DT_WORDBREAK); + } + + d->y_message = r.top; + } + + // and the suggestion + if (d->alert->suggestion) { + RECT r, ro; + int height; + size_t cch; + HICON h_sug_ico; + + r.left = d->x_message; + r.top = d->y_message + d->dy_message + d->dy_margin; + r.right = r.left + d->dx_message; + r.bottom = r.top + d->dy_suggestion; + + CopyRect(&ro, &r); + + // adjust for icon and padding + r.left += SM_CXICON + d->dx_suggest_pad * 2; + r.top += d->dx_suggest_pad; + r.right -= d->dx_suggest_pad; + r.bottom -= d->dx_suggest_pad; + + StringCchLength(d->alert->suggestion, + KHUI_MAXCCH_SUGGESTION, &cch); + + height = DrawText(hdc, + d->alert->suggestion, + (int) cch, + &r, + DT_WORDBREAK | + DT_CALCRECT); + + if (height > d->dy_suggestion) { + d->dy_suggestion = height; + need_resize = TRUE; + } else { + int old_bk_mode; + + ro.bottom = r.bottom + d->dx_suggest_pad; + + FillRect(hdc, &ro, (HBRUSH) (COLOR_INFOBK + 1)); + DrawEdge(hdc, &ro, EDGE_SUNKEN, BF_FLAT | BF_RECT); + + h_sug_ico = + LoadImage(0, + MAKEINTRESOURCE(OIC_INFORMATION), + IMAGE_ICON, + SM_CXICON, SM_CYICON, + LR_SHARED); + + assert(h_sug_ico != NULL); + + DrawIconEx(hdc, + ro.left + d->dx_suggest_pad, + ro.top + d->dx_suggest_pad, + h_sug_ico, + SM_CXICON, SM_CYICON, + 0, NULL, + DI_NORMAL); + + old_bk_mode = SetBkMode(hdc, TRANSPARENT); + + DrawText(hdc, + d->alert->suggestion, + (int) cch, + &r, + DT_WORDBREAK); + + SetBkMode(hdc, old_bk_mode); + } + + d->y_suggestion = r.top; + } + + khui_alert_unlock(d->alert); + + SelectObject(hdc, hf_old); + + EndPaint(hwnd, &ps); + + if (need_resize) { + RECT r; + int x,y; + int width, height; + int i; + + GetClientRect(hwnd, &r); + + height = max(d->dy_icon, + d->dy_message + + ((d->dy_suggestion > 0)? + (d->dy_suggestion + d->dy_margin): + 0)) + + d->dy_margin * 3 + d->dy_bb; + r.bottom = r.top + height; + + d->dy_client = height; + + AdjustWindowRectEx(&r, + GetWindowLongPtr(hwnd, GWL_STYLE), + FALSE, + GetWindowLongPtr(hwnd, GWL_EXSTYLE)); + + SetWindowPos(hwnd, + NULL, + 0, 0, + r.right - r.left, + r.bottom - r.top, + SWP_NOACTIVATE | SWP_NOCOPYBITS | + SWP_NOMOVE | SWP_NOOWNERZORDER | + SWP_NOZORDER); + + x = d->x_message; + y = d->dy_client - d->dy_bb; + width = d->dx_button; + height = d->dy_button; + + for(i=0; i<d->alert->n_alert_commands; i++) { + MoveWindow(d->hwnd_buttons[i], + x,y, + width,height, + TRUE); + + x += d->dx_button_incr; + } + } + + return FALSE; + } + break; /* not reached */ + + case WM_COMMAND: + { + alerter_wnd_data * d; + + d = (alerter_wnd_data *)(LONG_PTR) + GetWindowLongPtr(hwnd, NTF_PARAM); + + if(HIWORD(wParam) == BN_CLICKED) { + khui_alert_lock(d->alert); + d->alert->response = LOWORD(wParam); + khui_alert_unlock(d->alert); + + khm_leave_modal(); + + DestroyWindow(hwnd); + return 0; + } + } + break; + } + + return DefDlgProc(hwnd, uMsg, wParam, lParam); + //return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +ATOM khui_register_alerter_wnd_class(void) +{ + WNDCLASSEX wcx; + + ZeroMemory(&wcx, sizeof(wcx)); + + wcx.cbSize = sizeof(wcx); + wcx.style = CS_DROPSHADOW | CS_OWNDC; + wcx.lpfnWndProc = alerter_wnd_proc; + wcx.cbClsExtra = 0; + wcx.cbWndExtra = DLGWINDOWEXTRA + sizeof(LONG_PTR); + wcx.hInstance = khm_hInstance; + wcx.hIcon = LoadIcon(khm_hInstance, MAKEINTRESOURCE(IDI_MAIN_APP)); + wcx.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)); + wcx.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1); + wcx.lpszMenuName = NULL; + wcx.lpszClassName = KHUI_ALERTER_CLASS; + wcx.hIconSm = NULL; + + atom_alerter = RegisterClassEx(&wcx); + + return atom_alerter; +} + +/********************************************************************** + Notification Icon +***********************************************************************/ + +#define KHUI_NOTIFY_ICON_ID 0 + +void khui_notify_icon_add(void) { + NOTIFYICONDATA ni; + wchar_t buf[256]; + + ZeroMemory(&ni, sizeof(ni)); + + ni.cbSize = sizeof(ni); + ni.hWnd = hwnd_notifier; + ni.uID = KHUI_NOTIFY_ICON_ID; + ni.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; + ni.hIcon = LoadIcon(khm_hInstance, MAKEINTRESOURCE(IDI_NOTIFY_NONE)); + ni.uCallbackMessage = KHUI_WM_NOTIFIER; + LoadString(khm_hInstance, IDS_NOTIFY_PREFIX, buf, ARRAYLENGTH(buf)); + StringCbCopy(ni.szTip, sizeof(ni.szTip), buf); + LoadString(khm_hInstance, IDS_NOTIFY_READY, buf, ARRAYLENGTH(buf)); + StringCbCat(ni.szTip, sizeof(ni.szTip), buf); + + Shell_NotifyIcon(NIM_ADD, &ni); + + ni.cbSize = sizeof(ni); + ni.uVersion = NOTIFYICON_VERSION; + Shell_NotifyIcon(NIM_SETVERSION, &ni); + + DestroyIcon(ni.hIcon); +} + +void +khui_notify_icon_balloon(khm_int32 severity, + wchar_t * title, + wchar_t * msg, + khm_int32 timeout) { + NOTIFYICONDATA ni; + int iid; + + if (!msg || !title) + return; + + ZeroMemory(&ni, sizeof(ni)); + ni.cbSize = sizeof(ni); + + if (severity == KHERR_INFO) { + ni.dwInfoFlags = NIIF_INFO; + iid = IDI_NOTIFY_INFO; + } else if (severity == KHERR_WARNING) { + ni.dwInfoFlags = NIIF_WARNING; + iid = IDI_NOTIFY_WARN; + } else if (severity == KHERR_ERROR) { + ni.dwInfoFlags = NIIF_ERROR; + iid = IDI_NOTIFY_ERROR; + } else { + ni.dwInfoFlags = NIIF_NONE; + iid = IDI_NOTIFY_NONE; + } + + ni.hWnd = hwnd_notifier; + ni.uID = KHUI_NOTIFY_ICON_ID; + ni.uFlags = NIF_INFO | NIF_ICON; + ni.hIcon = LoadIcon(khm_hInstance, MAKEINTRESOURCE(iid)); + + if (FAILED(StringCbCopy(ni.szInfo, sizeof(ni.szInfo), msg))) { + /* too long? */ + StringCchCopyN(ni.szInfo, ARRAYLENGTH(ni.szInfo), + msg, + ARRAYLENGTH(ni.szInfo) - ARRAYLENGTH(ELIPSIS)); + StringCchCat(ni.szInfo, ARRAYLENGTH(ni.szInfo), + ELIPSIS); + } + + if (FAILED(StringCbCopy(ni.szInfoTitle, sizeof(ni.szInfoTitle), + title))) { + StringCchCopyN(ni.szInfoTitle, ARRAYLENGTH(ni.szInfoTitle), + title, + ARRAYLENGTH(ni.szInfoTitle) - ARRAYLENGTH(ELIPSIS)); + StringCchCat(ni.szInfoTitle, ARRAYLENGTH(ni.szInfoTitle), + ELIPSIS); + } + ni.uTimeout = timeout; + + Shell_NotifyIcon(NIM_MODIFY, &ni); + + DestroyIcon(ni.hIcon); +} + +void khui_notify_icon_change(khm_int32 severity) { + NOTIFYICONDATA ni; + wchar_t buf[256]; + int iid; + + if (severity == KHERR_INFO) + iid = IDI_NOTIFY_INFO; + else if (severity == KHERR_WARNING) + iid = IDI_NOTIFY_WARN; + else if (severity == KHERR_ERROR) + iid = IDI_NOTIFY_ERROR; + else + iid = IDI_NOTIFY_NONE; + + ZeroMemory(&ni, sizeof(ni)); + + ni.cbSize = sizeof(ni); + ni.hWnd = hwnd_notifier; + ni.uID = KHUI_NOTIFY_ICON_ID; + ni.uFlags = NIF_ICON | NIF_TIP; + ni.hIcon = LoadIcon(khm_hInstance, MAKEINTRESOURCE(iid)); + LoadString(khm_hInstance, IDS_NOTIFY_PREFIX, buf, ARRAYLENGTH(buf)); + StringCbCopy(ni.szTip, sizeof(ni.szTip), buf); + if(severity == KHERR_NONE) + LoadString(khm_hInstance, IDS_NOTIFY_READY, buf, ARRAYLENGTH(buf)); + else + LoadString(khm_hInstance, IDS_NOTIFY_ATTENTION, buf, ARRAYLENGTH(buf)); + StringCbCat(ni.szTip, sizeof(ni.szTip), buf); + + Shell_NotifyIcon(NIM_MODIFY, &ni); + + DestroyIcon(ni.hIcon); +} + +void khui_notify_icon_remove(void) { + NOTIFYICONDATA ni; + + ZeroMemory(&ni, sizeof(ni)); + + ni.cbSize = sizeof(ni); + ni.hWnd = hwnd_notifier; + ni.uID = KHUI_NOTIFY_ICON_ID; + + Shell_NotifyIcon(NIM_DELETE, &ni); +} + +/********************************************************************* + Initialization +**********************************************************************/ + +void khui_init_notifier(void) +{ + if(!khui_register_notifier_wnd_class()) + return; + + if(!khui_register_alerter_wnd_class()) + return; + + hwnd_notifier = CreateWindowEx(0, + MAKEINTATOM(atom_notifier), + KHUI_NOTIFIER_WINDOW, + 0, + 0,0,0,0, + HWND_MESSAGE, + NULL, + khm_hInstance, + NULL); + + if(hwnd_notifier != NULL) { + kmq_subscribe_hwnd(KMSG_ALERT, hwnd_notifier); + kmq_subscribe_hwnd(KMSG_CRED, hwnd_notifier); + notifier_ready = TRUE; + + khui_notify_icon_add(); + } +#ifdef DEBUG + else { + assert(hwnd_notifier != NULL); + } +#endif + khm_timer_init(); +} + +void khui_exit_notifier(void) +{ + khm_timer_exit(); + + if(hwnd_notifier != NULL) { + khui_notify_icon_remove(); + kmq_unsubscribe_hwnd(KMSG_ALERT, hwnd_notifier); + kmq_unsubscribe_hwnd(KMSG_CRED, hwnd_notifier); + DestroyWindow(hwnd_notifier); + hwnd_notifier = NULL; + } + + if(atom_notifier != 0) { + UnregisterClass(MAKEINTATOM(atom_notifier), khm_hInstance); + atom_notifier = 0; + } + + if(atom_alerter != 0) { + UnregisterClass(MAKEINTATOM(atom_alerter), khm_hInstance); + atom_alerter = 0; + } + + notifier_ready = FALSE; +} diff --git a/src/windows/identity/ui/notifier.h b/src/windows/identity/ui/notifier.h new file mode 100644 index 000000000..bfe9656b8 --- /dev/null +++ b/src/windows/identity/ui/notifier.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_NOTIFIER_H +#define __KHIMAIRA_NOTIFIER_H + +void +khui_init_notifier(void); + +void +khui_exit_notifier(void); + +void +khui_notify_icon_change(khm_int32 severity); + +void +khui_notify_icon_balloon(khm_int32 severity, + wchar_t * title, + wchar_t * msg, + khm_int32 timeout); + +#endif diff --git a/src/windows/identity/ui/passwnd.c b/src/windows/identity/ui/passwnd.c new file mode 100644 index 000000000..4084ede41 --- /dev/null +++ b/src/windows/identity/ui/passwnd.c @@ -0,0 +1,133 @@ +#include<khmapp.h> + +static ATOM sAtom = 0; +static HINSTANCE shInstance = 0; + +/* Callback for the MITPasswordControl +This is a replacement for the normal edit control. It does not show the +annoying password char in the edit box so that the number of chars in the +password are not known. +*/ + +#define PASSWORDCHAR L'#' +#define DLGHT(ht) (HIWORD(GetDialogBaseUnits())*(ht)/8) +#define DLGWD(wd) (LOWORD(GetDialogBaseUnits())*(wd)/4) + +static +LRESULT +CALLBACK +MITPasswordEditProc( + HWND hWnd, + UINT message, + WPARAM wParam, + LPARAM lParam + ) +{ + static SIZE pwdcharsz; + BOOL pass_the_buck = FALSE; + + if (message > WM_USER && message < 0x7FFF) + pass_the_buck = TRUE; + + switch(message) + { + case WM_GETTEXT: + case WM_GETTEXTLENGTH: + case WM_SETTEXT: + pass_the_buck = TRUE; + break; + case WM_PAINT: + { + HDC hdc; + PAINTSTRUCT ps; + RECT r; + + hdc = BeginPaint(hWnd, &ps); + GetClientRect(hWnd, &r); + Rectangle(hdc, 0, 0, r.right, r.bottom); + EndPaint(hWnd, &ps); + } + break; + case WM_SIZE: + { + MoveWindow(GetDlgItem(hWnd, 1), DLGWD(2), DLGHT(2), + pwdcharsz.cx / 2, pwdcharsz.cy, TRUE); + } + break; + case WM_LBUTTONDOWN: + case WM_SETFOCUS: + { + SetFocus(GetDlgItem(hWnd, 1)); + } + break; + case WM_CREATE: + { + HWND heditchild; + wchar_t pwdchar = PASSWORDCHAR; + HDC hdc; + /* Create a child window of this control for default processing. */ + hdc = GetDC(hWnd); + GetTextExtentPoint32(hdc, &pwdchar, 1, &pwdcharsz); + ReleaseDC(hWnd, hdc); + + heditchild = + CreateWindow(L"edit", L"", WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | + ES_LEFT | ES_PASSWORD | WS_TABSTOP, + 0, 0, 0, 0, + hWnd, + (HMENU)1, + ((LPCREATESTRUCT)lParam)->hInstance, + NULL); + SendMessage(heditchild, EM_SETPASSWORDCHAR, PASSWORDCHAR, 0L); + } + break; + } + + if (pass_the_buck) + return SendMessage(GetDlgItem(hWnd, 1), message, wParam, lParam); + return DefWindowProc(hWnd, message, wParam, lParam); +} + +khm_int32 +khm_register_passwnd_class(void) +{ + if (!sAtom) { + WNDCLASS wndclass; + + memset(&wndclass, 0, sizeof(WNDCLASS)); + + shInstance = khm_hInstance; + + wndclass.style = CS_HREDRAW | CS_VREDRAW; + wndclass.lpfnWndProc = (WNDPROC)MITPasswordEditProc; + wndclass.cbClsExtra = sizeof(HWND); + wndclass.cbWndExtra = 0; + wndclass.hInstance = shInstance; + wndclass.hbrBackground = (void *)(COLOR_WINDOW + 1); + wndclass.lpszClassName = MIT_PWD_DLL_CLASS; + wndclass.hCursor = LoadCursor((HINSTANCE)NULL, IDC_IBEAM); + + sAtom = RegisterClass(&wndclass); + } + + return (sAtom)?KHM_ERROR_SUCCESS:KHM_ERROR_UNKNOWN; +} + +khm_int32 +khm_unregister_passwnd_class(void) +{ + BOOL result = TRUE; + + if ((khm_hInstance != shInstance) || !sAtom) { + return KHM_ERROR_INVALID_OPERATION; + } + + result = UnregisterClass(MIT_PWD_DLL_CLASS, khm_hInstance); + if (result) { + sAtom = 0; + shInstance = 0; + return KHM_ERROR_SUCCESS; + } else { + return KHM_ERROR_UNKNOWN; + } +} diff --git a/src/windows/identity/ui/passwnd.h b/src/windows/identity/ui/passwnd.h new file mode 100644 index 000000000..f8c17f68e --- /dev/null +++ b/src/windows/identity/ui/passwnd.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_PASSWND_H +#define __KHIMAIRA_PASSWND_H + +/* Declarations for the MIT password change control. Functionally the + same as the regular Windows password edit control but doesn't + display the '*' password character. */ + +#define MIT_PWD_DLL_CLASS L"MITPasswordWnd" + +khm_int32 khm_unregister_passwnd_class(void); +khm_int32 khm_register_passwnd_class(void); + +#endif diff --git a/src/windows/identity/ui/propertywnd.c b/src/windows/identity/ui/propertywnd.c new file mode 100644 index 000000000..fe603c015 --- /dev/null +++ b/src/windows/identity/ui/propertywnd.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khmapp.h> +#include<assert.h> + +typedef struct tag_pw_data { + khm_handle record; + HWND hwnd_lv; +} pw_data; + +ATOM khui_propertywnd_cls; + +#define ID_LISTVIEW 1 + +#define PW_WM_SET_RECORD WM_USER + +void pw_update_property_data(HWND hw, pw_data * d) +{ + HWND hwnd_lv; + khm_int32 * attrs = NULL; + + hwnd_lv = d->hwnd_lv; + + if(hwnd_lv == NULL) + return; + + ListView_DeleteAllItems(hwnd_lv); + + if(d->record != NULL) { + wchar_t * buffer; + khm_size attr_count; + khm_size i; + khm_size cb_buf; + khm_size t; + LVITEM lvi; + int idx; + + if(KHM_FAILED(kcdb_attrib_get_count( + KCDB_ATTR_FLAG_VOLATILE | + KCDB_ATTR_FLAG_HIDDEN, + 0, + &attr_count))) + return; + + attrs = malloc(sizeof(khm_int32) * attr_count); + assert(attrs != NULL); + + kcdb_attrib_get_ids( + KCDB_ATTR_FLAG_VOLATILE | + KCDB_ATTR_FLAG_HIDDEN, + 0, + attrs, + &attr_count); + + cb_buf = sizeof(wchar_t) * 2048; + buffer = malloc(cb_buf); + assert(buffer != NULL); + + for(i=0; i<attr_count; i++) { + if(KHM_FAILED(kcdb_buf_get_attr(d->record, attrs[i], NULL, NULL, NULL))) + continue; + + ZeroMemory(&lvi, sizeof(lvi)); + lvi.mask = LVIF_TEXT | LVIF_PARAM; + lvi.iItem = (int) i; + lvi.iSubItem = 0; + lvi.pszText = buffer; + lvi.lParam = (LPARAM) attrs[i]; + + t = cb_buf; + kcdb_attrib_describe(attrs[i], buffer, &t, KCDB_TS_SHORT); + + idx = ListView_InsertItem(hwnd_lv, &lvi); + + ZeroMemory(&lvi, sizeof(lvi)); + lvi.mask = LVIF_TEXT; + lvi.iItem = idx; + lvi.iSubItem = 1; + lvi.pszText = buffer; + + t = cb_buf; + kcdb_buf_get_attr_string(d->record, attrs[i], buffer, &t, 0); + + ListView_SetItem(hwnd_lv, &lvi); + } + + free(attrs); + free(buffer); + } +} + +LRESULT CALLBACK khui_property_wnd_proc( + HWND hwnd, + UINT msg, + WPARAM wParam, + LPARAM lParam) +{ + BOOL child_msg = FALSE; + pw_data * child; + + switch(msg) { + case WM_CREATE: + { + CREATESTRUCT * cs; + LVCOLUMN lvc; + wchar_t sz_title[256]; + + cs = (CREATESTRUCT *) lParam; + + child = malloc(sizeof(*child)); + ZeroMemory(child, sizeof(*child)); + +#pragma warning(push) +#pragma warning(disable:4244) + SetWindowLongPtr(hwnd, 0, (LONG_PTR) child); +#pragma warning(pop) + + child->hwnd_lv = CreateWindow( + WC_LISTVIEW, + L"", + WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | + LVS_REPORT | LVS_SORTASCENDING, + 0, 0, + cs->cx, cs->cy, + hwnd, + (HMENU) ID_LISTVIEW, + khm_hInstance, + NULL); + + ListView_SetExtendedListViewStyle(child->hwnd_lv, + LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); + + ZeroMemory(&lvc, sizeof(lvc)); + lvc.mask = LVCF_FMT | LVCF_ORDER | LVCF_TEXT | LVCF_WIDTH; + lvc.fmt = LVCFMT_LEFT; + lvc.cx = (cs->cx * 2)/ 5; + lvc.pszText = sz_title; + lvc.iSubItem = 0; + lvc.iOrder = 0; + LoadString(khm_hInstance, IDS_PROP_COL_PROPERTY, sz_title, ARRAYLENGTH(sz_title)); + + ListView_InsertColumn(child->hwnd_lv, 0, &lvc); + + ZeroMemory(&lvc, sizeof(lvc)); + lvc.mask = LVCF_FMT | LVCF_ORDER | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH; + lvc.fmt = LVCFMT_LEFT; + lvc.cx = (cs->cx * 3)/ 5; + lvc.pszText = sz_title; + lvc.iSubItem = 1; + lvc.iOrder = 1; + LoadString(khm_hInstance, IDS_PROP_COL_VALUE, sz_title, ARRAYLENGTH(sz_title)); + + ListView_InsertColumn(child->hwnd_lv, 1, &lvc); + + if(cs->lpCreateParams != NULL) { + child->record = cs->lpCreateParams; + kcdb_buf_hold(child->record); + pw_update_property_data(hwnd, child); + } + } + break; + + case PW_WM_SET_RECORD: + { + child = (pw_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + kcdb_buf_release(child->record); + child->record = (khm_handle) lParam; + kcdb_buf_hold(child->record); + pw_update_property_data(hwnd, child); + } + return 0; + + case WM_DESTROY: + { + child = (pw_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + kcdb_buf_release(child->record); + free(child); + } + break; + + case WM_PAINT: + break; + + default: + child = (pw_data *)(LONG_PTR) GetWindowLongPtr(hwnd, 0); + child_msg = TRUE; + } + + /* + if(child_msg && child && child->hwnd_lv) + return SendMessage(child->hwnd_lv, msg, wParam, lParam); + else + */ + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +khm_int32 khm_register_propertywnd_class(void) +{ + WNDCLASSEX wcx; + + wcx.cbSize = sizeof(wcx); + wcx.style = CS_DBLCLKS; + wcx.lpfnWndProc = khui_property_wnd_proc; + wcx.cbClsExtra = 0; + wcx.cbWndExtra = sizeof(LONG_PTR); + wcx.hInstance = khm_hInstance; + wcx.hIcon = NULL; + wcx.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW); + wcx.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1); + wcx.lpszMenuName = NULL; + wcx.lpszClassName = KHUI_PROPERTYWND_CLASS_NAME; + wcx.hIconSm = NULL; + + khui_propertywnd_cls = RegisterClassEx(&wcx); + + return (khui_propertywnd_cls == 0)?KHM_ERROR_UNKNOWN:KHM_ERROR_SUCCESS; +} + +khm_int32 khm_unregister_propertywnd_class(void) +{ + UnregisterClass(MAKEINTATOM(khui_propertywnd_cls), khm_hInstance); + + return KHM_ERROR_SUCCESS; +} diff --git a/src/windows/identity/ui/propertywnd.h b/src/windows/identity/ui/propertywnd.h new file mode 100644 index 000000000..67250c96d --- /dev/null +++ b/src/windows/identity/ui/propertywnd.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_PROPERTYWND_H +#define __KHIMAIRA_PROPERTYWND_H + +#define KHUI_PROPERTYWND_CLASS_NAME L"NetIDMgrPropertyWnd" + +khm_int32 khm_register_propertywnd_class(void); + +khm_int32 khm_unregister_propertywnd_class(void); + +#endif diff --git a/src/windows/identity/ui/reqdaemon.c b/src/windows/identity/ui/reqdaemon.c new file mode 100644 index 000000000..b73ec4820 --- /dev/null +++ b/src/windows/identity/ui/reqdaemon.c @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khmapp.h> +#include<assert.h> + +ATOM reqdaemon_atom = 0; +HANDLE reqdaemon_thread = NULL; +HWND reqdaemon_hwnd = NULL; + +LRESULT CALLBACK +reqdaemonwnd_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + + switch(uMsg) { + case WM_CREATE: + break; + + case WM_CLOSE: + DestroyWindow(hwnd); + break; + + case WM_DESTROY: + reqdaemon_hwnd = NULL; + PostQuitMessage(0); + break; + + /* Leash compatibility */ + case ID_OBTAIN_TGT_WITH_LPARAM: + { + wchar_t widname[KCDB_IDENT_MAXCCH_NAME]; + wchar_t wmapping[ARRAYLENGTH(KHUI_REQD_MAPPING_FORMAT) + 10]; + khm_handle identity = NULL; + LPNETID_DLGINFO pdlginfo; + LRESULT lr = 1; + khm_int32 result; + HANDLE hmap = NULL; + HRESULT hr; + + hr = StringCbPrintf(wmapping, sizeof(wmapping), + KHUI_REQD_MAPPING_FORMAT, (DWORD) lParam); +#ifdef DEBUG + assert(SUCCEEDED(hr)); +#endif + hmap = CreateFileMapping(INVALID_HANDLE_VALUE, + NULL, + PAGE_READWRITE, + 0, 4096, + wmapping); + + if (hmap == NULL) { + return -1; + } else if (hmap != NULL && GetLastError() != ERROR_ALREADY_EXISTS) { + CloseHandle(hmap); + return -1; + } + + pdlginfo = MapViewOfFile(hmap, + FILE_MAP_WRITE, + 0, 0, + sizeof(*pdlginfo)); + + if (pdlginfo == NULL) { + CloseHandle(hmap); + return 1; + } + + if (pdlginfo->in.username[0] && + pdlginfo->in.realm[0] && + SUCCEEDED(StringCbPrintf(widname, + sizeof(widname), + L"%s@%s", + pdlginfo->in.username, + pdlginfo->in.realm))) { + + kcdb_identity_create(widname, + KCDB_IDENT_FLAG_CREATE, + &identity); + + } + + do { + if (khm_cred_is_in_dialog()) { + khm_cred_wait_for_dialog(INFINITE, NULL); + } + + if (identity) + khui_context_set_ex(KHUI_SCOPE_IDENT, + identity, + KCDB_CREDTYPE_INVALID, + NULL, + NULL, + 0, + NULL, + pdlginfo, + sizeof(*pdlginfo)); + else + khui_context_reset(); + + + if (pdlginfo->dlgtype == NETID_DLGTYPE_TGT) + SendMessage(khm_hwnd_main, WM_COMMAND, + MAKEWPARAM(KHUI_ACTION_NEW_CRED, 0), 0); + else if (pdlginfo->dlgtype == NETID_DLGTYPE_CHPASSWD) + SendMessage(khm_hwnd_main, WM_COMMAND, + MAKEWPARAM(KHUI_ACTION_PASSWD_ID, 0), 0); + else + break; + + if (KHM_FAILED(khm_cred_wait_for_dialog(INFINITE, &result))) + continue; + else { + lr = (result != KHUI_NC_RESULT_GET_CREDS); + break; + } + } while(TRUE); + + if (pdlginfo) + UnmapViewOfFile(pdlginfo); + if (hmap) + CloseHandle(hmap); + + return lr; + } + +#if 0 + /* deprecated */ + case ID_OBTAIN_TGT_WITH_LPARAM: + { + char * param = (char *) GlobalLock((HGLOBAL) lParam); + char * username = NULL; + char * realm = NULL; + char * title = NULL; + char * ccache = NULL; + wchar_t widname[KCDB_IDENT_MAXCCH_NAME]; + wchar_t wtitle[KHUI_MAXCCH_TITLE]; + size_t cch; + khm_int32 rv = KHM_ERROR_SUCCESS; + khm_handle identity = NULL; + NETID_DLGINFO dlginfo; + + if (param) { + if (*param) + title = param; + + if (FAILED(StringCchLengthA(param, KHUI_MAXCCH_TITLE, &cch))) { +#ifdef DEBUG + assert(FALSE); +#endif + rv = KHM_ERROR_INVALID_PARM; + goto _exit_tgt_with_lparam; + } + + param += cch + 1; + + if (*param) + username = param; + + if (FAILED(StringCchLengthA(param, KCDB_IDENT_MAXCCH_NAME, &cch))) { +#ifdef DEBUG + assert(FALSE); +#endif + rv = KHM_ERROR_INVALID_PARM; + goto _exit_tgt_with_lparam; + } + + param += cch + 1; + + if (*param) + realm = param; + + if (FAILED(StringCchLengthA(param, KCDB_IDENT_MAXCCH_NAME, &cch))) { +#ifdef DEBUG + assert(FALSE); +#endif + rv = KHM_ERROR_INVALID_PARM; + goto _exit_tgt_with_lparam; + } + + param += cch + 1; + + if (*param) + ccache = param; + } + + if (username && realm) { + + if (FAILED(StringCbPrintf(widname, sizeof(widname), + L"%hs@%hs", username, realm))) { + rv = KHM_ERROR_INVALID_PARM; + goto _exit_tgt_with_lparam; + } + + rv = kcdb_identity_create(widname, + KCDB_IDENT_FLAG_CREATE, + &identity); + if (KHM_FAILED(rv)) { + goto _exit_tgt_with_lparam; + } + } + + ZeroMemory(&dlginfo, sizeof(dlginfo)); + + dlginfo.size = NETID_DLGINFO_V1_SZ; + dlginfo.dlgtype = NETID_DLGTYPE_TGT; + + if (title) + StringCbCopy(dlginfo.in.title, sizeof(dlginfo.in.title), + wtitle); + if (username) + AnsiStrToUnicode(dlginfo.in.username, sizeof(dlginfo.in.username), + username); + if (realm) + AnsiStrToUnicode(dlginfo.in.realm, sizeof(dlginfo.in.realm), + realm); + + if (ccache) + AnsiStrToUnicode(dlginfo.in.ccache, sizeof(dlginfo.in.ccache), + ccache); + + dlginfo.in.use_defaults = TRUE; + + do { + if (khm_cred_is_in_dialog()) { + khm_cred_wait_for_dialog(INFINITE); + } + + khui_context_set_ex(KHUI_SCOPE_IDENT, + identity, + KCDB_CREDTYPE_INVALID, + NULL, + NULL, + 0, + NULL, + &dlginfo, + sizeof(dlginfo)); + + if (title) { + AnsiStrToUnicode(wtitle, sizeof(wtitle), + title); + + khm_cred_obtain_new_creds(wtitle); + } else { + khm_cred_obtain_new_creds(NULL); + } + + if (KHM_FAILED(khm_cred_wait_for_dialog(INFINITE))) + continue; + else + break; + } while(TRUE); + + _exit_tgt_with_lparam: + if (identity) + kcdb_identity_release(identity); + + GlobalUnlock((HGLOBAL) lParam); + } + return 0; +#endif + + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +DWORD WINAPI +khm_reqdaemon_thread_proc(LPVOID vparam) { + BOOL rv; + MSG msg; + DWORD dw; + + khm_register_reqdaemonwnd_class(); + +#ifdef DEBUG + assert(reqdaemon_atom != 0); +#endif + + reqdaemon_hwnd = CreateWindowEx(0, + MAKEINTATOM(reqdaemon_atom), + KHUI_REQDAEMONWND_NAME, + 0, + 0,0,0,0, + HWND_MESSAGE, + NULL, + khm_hInstance, + NULL); + +#ifdef DEBUG + dw = GetLastError(); + assert(reqdaemon_hwnd != NULL); +#endif + + while(rv = GetMessage(&msg, NULL, 0, 0)) { + if (rv == -1) { +#ifdef DEBUG + assert(FALSE); +#endif + break; + } else { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + reqdaemon_thread = NULL; + + khm_unregister_reqdaemonwnd_class(); + + return 0; +} + +void +khm_register_reqdaemonwnd_class(void) { + WNDCLASSEX wcx; + + ZeroMemory(&wcx, sizeof(wcx)); + + wcx.cbSize = sizeof(wcx); + wcx.style = 0; + wcx.lpfnWndProc = reqdaemonwnd_proc; + wcx.cbClsExtra = 0; + wcx.cbWndExtra = 0; + wcx.hInstance = khm_hInstance; + wcx.hIcon = NULL; + wcx.hCursor = NULL; + wcx.hbrBackground = NULL; + wcx.lpszMenuName = NULL; + wcx.lpszClassName = KHUI_REQDAEMONWND_CLASS; + wcx.hIconSm = NULL; + + reqdaemon_atom = RegisterClassEx(&wcx); + +#ifdef DEBUG + assert(reqdaemon_atom != 0); +#endif +} + +void +khm_unregister_reqdaemonwnd_class(void) { + if (reqdaemon_atom != 0) { + UnregisterClass(MAKEINTATOM(reqdaemon_atom), khm_hInstance); + reqdaemon_atom = 0; + } +} + +void +khm_init_request_daemon(void) { +#ifdef DEBUG + assert(reqdaemon_thread == NULL); +#endif + + reqdaemon_thread = CreateThread(NULL, + 0, + khm_reqdaemon_thread_proc, + NULL, + 0, + NULL); + +#ifdef DEBUG + assert(reqdaemon_thread != NULL); +#endif +} + +void +khm_exit_request_daemon(void) { + if (reqdaemon_hwnd == NULL) + return; + + SendMessage(reqdaemon_hwnd, WM_CLOSE, 0, 0); +} diff --git a/src/windows/identity/ui/reqdaemon.h b/src/windows/identity/ui/reqdaemon.h new file mode 100644 index 000000000..13b0ebd2c --- /dev/null +++ b/src/windows/identity/ui/reqdaemon.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_REQDAEMON_H +#define __KHIMAIRA_REQDAEMON_H + +void +khm_register_reqdaemonwnd_class(void); + +void +khm_unregister_reqdaemonwnd_class(void); + +void +khm_init_request_daemon(void); + +void +khm_exit_request_daemon(void); + +#endif diff --git a/src/windows/identity/ui/resource.h b/src/windows/identity/ui/resource.h new file mode 100644 index 000000000..a58cebb8d --- /dev/null +++ b/src/windows/identity/ui/resource.h @@ -0,0 +1,313 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by D:\work\khimaira\src\ui\lang\en_us\khapp.rc +// +#define IDI_MAIN_APP 104 +#define IDD_PROPPAGE_MEDIUM 106 +#define IDD_PP_CRED 106 +#define IDD_PP_IDENT 107 +#define IDB_TK_REFRESH 108 +#define IDS_MAIN_WINDOW_TITLE 108 +#define IDS_MENU_FILE 109 +#define IDB_ID 110 +#define IDS_MENU_IDENTITY 110 +#define IDS_MENU_CRED 110 +#define IDB_ID_DELETE 111 +#define IDS_MENU_VIEW 111 +#define IDB_ID_NEW 112 +#define IDS_MENU_OPTIONS 112 +#define IDB_ID_REFRESH 113 +#define IDS_MENU_HELP 113 +#define IDB_TK 114 +#define IDS_ACTION_PROPERTIES 114 +#define IDB_TK_DELETE 115 +#define IDS_ACTION_EXIT 115 +#define IDB_TK_NEW 116 +#define IDS_ACTION_NEW_ID 116 +#define IDS_CFG_ROOT_NAME 116 +#define IDS_ACTION_SET_DEF_ID 117 +#define IDS_ACTION_SET_SRCH_ID 118 +#define IDB_VW_REFRESH_SM 118 +#define IDS_ACTION_DESTROY_ID 119 +#define IDR_MENU_BAR 119 +#define IDS_CFG_ROOT_TITLE 119 +#define IDS_ACTION_RENEW_ID 120 +#define IDS_CFG_GENERAL_SHORT 120 +#define IDS_ACTION_ADD_CRED 121 +#define IDB_TB_BLANK 121 +#define IDS_ACTION_NEW_CRED 121 +#define IDS_ACTION_PASSWD_ID 122 +#define IDS_ACTION_CHOOSE_COLS 123 +#define IDB_TB_BLANK_SM 123 +#define IDS_ACTION_DEBUG_WINDOW 124 +#define IDB_VW_REFRESH 124 +#define IDS_ACTION_VIEW_REFRESH 125 +#define IDB_ID_DELETE_DIS 125 +#define IDS_MENU_LAYOUT 126 +#define IDB_ID_DELETE_DIS_SM 126 +#define IDS_MENU_TOOLBARS 127 +#define IDB_ID_DELETE_SM 127 +#define IDS_ACTION_LAYOUT_ID 128 +#define IDB_ID_DIS 128 +#define IDS_ACTION_LAYOUT_TYPE 129 +#define IDB_ID_DIS_SM 129 +#define IDS_ACTION_LAYOUT_LOC 130 +#define IDB_ID_NEW_DIS 130 +#define IDS_ACTION_TB_STANDARD 131 +#define IDB_ID_NEW_DIS_SM 131 +#define IDS_ACTION_OPT_KHIM 132 +#define IDB_ID_NEW_SM 132 +#define IDS_ACTION_OPT_INIT 133 +#define IDB_ID_REFRESH_DIS 133 +#define IDS_ACTION_OPT_IDENTS 133 +#define IDS_ACTION_OPT_NOTIF 134 +#define IDB_ID_REFRESH_SM 134 +#define IDS_ACTION_HELP_CTX 135 +#define IDB_ID_REFRESH_DIS_SM 135 +#define IDS_ACTION_HELP_CONTENTS 136 +#define IDB_TK_DELETE_DIS 136 +#define IDS_ACTION_HELP_INDEX 137 +#define IDB_TK_DELETE_DIS_SM 137 +#define IDS_ACTION_HELP_ABOUT 138 +#define IDB_TK_DELETE_SM 138 +#define IDB_TK_DIS_SM 139 +#define IDS_ACTIONINFO_NEW_ID 139 +#define IDS_CFG_GENERAL_LONG 139 +#define IDB_TK_NEW_DIS 140 +#define IDS_SAMPLE_STRING 140 +#define IDB_TK_NEW_DIS_SM 141 +#define IDS_NO_CREDS 141 +#define IDB_TK_NEW_SM 142 +#define IDS_WT_INIT_CREDS 142 +#define IDB_TK_REFRESH_DIS 143 +#define IDS_WT_NEW_CREDS 143 +#define IDB_TK_REFRESH_DIS_SM 144 +#define IDS_NC_PASSWORD 144 +#define IDS_NC_IDENTITY 144 +#define IDB_TK_REFRESH_SM 145 +#define IDS_NC_IDENTS 145 +#define IDB_TK_SM 146 +#define IDS_NC_CREDTEXT_ID_NONE 146 +#define IDB_HELP_SM 147 +#define IDS_NC_CREDTEXT_ID_ONE 147 +#define IDB_HELP 148 +#define IDS_NC_CREDTEXT_ID_MANY 148 +#define IDB_LOGO_SHADE 149 +#define IDS_NC_CREDTEXT_ID_INVALID 149 +#define IDI_WGT_COLLAPSE 150 +#define IDS_WTPOST_INIT_CREDS 150 +#define IDI_WGT_EXPAND 151 +#define IDS_WTPOST_NEW_CREDS 151 +#define IDB_WDG_EXPAND 152 +#define IDS_ACTION_RENEW_CRED 152 +#define IDB_WDG_COLLAPSE 153 +#define IDS_ACTION_DESTROY_CRED 153 +#define IDB_ID_SM 154 +#define IDS_DEFAULT_FONT 154 +#define IDB_WDG_EXPAND_HI 155 +#define IDS_NC_CREDTEXT_TABS 155 +#define IDB_WDG_COLLAPSE_HI 156 +#define IDS_NOTIFY_PREFIX 156 +#define IDB_WDG_CREDTYPE 157 +#define IDS_NOTIFY_READY 157 +#define IDB_WDG_FLAG 158 +#define IDS_NOTIFY_ATTENTION 158 +#define IDB_FLAG_WARN 159 +#define IDS_ALERT_DEFAULT 159 +#define IDB_FLAG_EXPIRED 160 +#define IDS_PACTION_OK 160 +#define IDB_FLAG_CRITICAL 161 +#define IDS_PACTION_CANCEL 161 +#define IDD_NC_PASSWORD 162 +#define IDS_PACTION_CLOSE 162 +#define IDD_NC_NEWCRED 162 +#define IDD_NC_BBAR 163 +#define IDS_ALERT_NOSEL_TITLE 163 +#define IDD_NC_TS 164 +#define IDS_ALERT_NOSEL 164 +#define IDI_ENABLED 165 +#define IDS_NC_CREDTEXT_ID_VALID 165 +#define IDI_DISABLED 166 +#define IDS_NC_CREDTEXT_ID_UNCHECKED 166 +#define IDS_PROP_COL_PROPERTY 167 +#define IDS_PROP_COL_VALUE 168 +#define IDI_NOTIFY_NONE 169 +#define IDS_NC_NEW_IDENT 169 +#define IDI_NOTIFY_INFO 170 +#define IDS_NC_CREDTEXT_ID_CHECKING 170 +#define IDI_NOTIFY_WARN 171 +#define IDS_ACTION_OPEN_APP 171 +#define IDI_NOTIFY_ERROR 172 +#define IDS_CTX_NEW_IDENT 172 +#define IDS_CTX_NEW_CREDS 173 +#define IDD_CFG_MAIN 173 +#define IDS_CTX_RENEW_CREDS 174 +#define IDD_CFG_GENERIC 174 +#define IDS_CTX_PROC_NEW_IDENT 175 +#define IDB_LOGO_OPAQUE 175 +#define IDS_CTX_PROC_NEW_CREDS 176 +#define IDD_CFG_GENERAL 176 +#define IDS_CTX_PROC_RENEW_CREDS 177 +#define IDD_CFG_IDENTITIES 177 +#define IDS_ACTION_CLOSE_APP 178 +#define IDD_CFG_NOTIF 178 +#define IDS_NC_FAILED_TITLE 179 +#define IDD_CFG_PLUGINS 179 +#define IDS_CFG_IDENTITIES_SHORT 180 +#define IDD_CFG_IDENTITY 180 +#define IDS_CFG_IDENTITIES_LONG 181 +#define IDI_CFG_DEFAULT 181 +#define IDS_CFG_NOTIF_SHORT 182 +#define IDI_CFG_MODIFIED 182 +#define IDS_CFG_NOTIF_LONG 183 +#define IDI_CFG_APPLIED 183 +#define IDS_CFG_PLUGINS_SHORT 184 +#define IDD_CFG_IDS_TAB 184 +#define IDS_CFG_PLUGINS_LONG 185 +#define IDD_CFG_ID_TAB 185 +#define IDS_CFG_IDENTITY_SHORT 186 +#define IDI_CFG_DELETED 186 +#define IDS_CFG_IDENTITY_LONG 187 +#define IDI_ICON1 187 +#define IDI_ID 187 +#define IDS_CTX_DESTROY_CREDS 188 +#define IDB_IMPORT_SM_DIS 188 +#define IDS_WARN_EXPIRE 189 +#define IDB_IMPORT 189 +#define IDS_WARN_TITLE 190 +#define IDB_IMPORT_DIS 190 +#define IDS_ALERT_MOREINFO 191 +#define IDB_IMPORT_SM 191 +#define IDS_WARN_EXPIRED 192 +#define IDB_CHPW_SM 192 +#define IDS_WARN_EXPIRE_ID 193 +#define IDB_CHPW 193 +#define IDS_WARN_EXPIRED_ID 194 +#define IDB_CHPW_DIS 194 +#define IDS_WARN_WM_TITLE 195 +#define IDB_CHPW_DIS_SM 195 +#define IDS_WARN_WM_MSG 196 +#define IDD_ABOUT 196 +#define IDS_CFG_ID_TAB_SHORT 197 +#define IDB_TB_SPACE 197 +#define IDS_CFG_ID_TAB_LONG 198 +#define IDS_CFG_IDS_TAB_SHORT 199 +#define IDS_CFG_IDS_TAB_LONG 200 +#define IDS_CFG_IDS_IDENTITY 201 +#define IDS_ACTION_IMPORT 202 +#define IDS_CTX_IMPORT 203 +#define IDS_CFG_PI_COL_PLUGINS 204 +#define IDS_PISTATE_FAILUNK 205 +#define IDS_PISTATE_FAILMAX 206 +#define IDS_PISTATE_FAILREG 207 +#define IDS_PISTATE_FAILDIS 208 +#define IDS_PISTATE_FAILLOD 209 +#define IDS_PISTATE_PLACEHOLD 210 +#define IDS_PISTATE_REG 211 +#define IDS_PISTATE_HOLD 212 +#define IDS_PISTATE_INIT 213 +#define IDS_PISTATE_RUN 214 +#define IDS_PISTATE_EXIT 215 +#define IDS_CTX_PASSWORD 216 +#define IDS_WT_PASSWORD 217 +#define IDS_WTPOST_PASSWORD 218 +#define IDS_CTX_PROC_PASSWORD 219 +#define IDS_NC_PWD_FAILED_TITLE 220 +#define IDS_CMDLINE_HELP 221 +#define IDC_NC_USERNAME 1007 +#define IDC_NC_PASSWORD 1008 +#define IDC_NC_CREDTEXT_LABEL 1009 +#define IDC_NC_PASSWORD_LABEL 1010 +#define IDC_NC_USERNAME_LABEL 1011 +#define IDC_NC_CREDTEXT 1012 +#define IDC_NC_HELP 1017 +#define IDC_NC_OPTIONS 1019 +#define IDC_PP_IDNAME 1026 +#define IDC_PP_IDDEF 1027 +#define IDC_PP_IDSEARCH 1028 +#define IDC_PP_IDSTATUS 1029 +#define IDC_PP_IDSTATUSIMG 1030 +#define IDC_PP_IDVALID 1031 +#define IDC_PP_IDRENEW 1032 +#define IDC_NC_IDENTITY 1033 +#define IDC_NC_IDENTITY_LABEL 1034 +#define IDC_PP_PROPLIST 1035 +#define IDC_PP_CPROPLIST 1036 +#define IDC_NC_REALM 1037 +#define IDC_NC_REALM_LABEL 1038 +#define IDC_NC_TPL_ROW 1039 +#define IDC_NC_TPL_PANEL 1040 +#define IDC_NC_TPL_LABEL 1041 +#define IDC_NC_TPL_INPUT 1042 +#define IDC_NC_TPL_LABEL_LG 1043 +#define IDC_NC_TPL_INPUT_LG 1044 +#define IDC_NC_TPL_ROW2 1045 +#define IDC_NC_TPL_ROW_LG 1045 +#define IDC_CFG_NODELIST 1045 +#define IDAPPLY 1048 +#define IDC_CFG_SUMMARY 1049 +#define IDC_CFG_TITLE 1050 +#define IDC_CFG_PANE 1051 +#define IDC_NOTIF_MONITOR 1053 +#define IDC_PP_DUMMY 1054 +#define IDC_NOTIF_RENEW 1055 +#define IDC_NOTIF_RENEW_THR 1056 +#define IDC_NOTIF_WARN1 1057 +#define IDC_NOTIF_WARN1_THR 1058 +#define IDC_NOTIF_WARN2 1059 +#define IDC_NOTIF_WARN2_THR 1060 +#define IDC_CFG_KEEPRUNNING 1061 +#define IDC_CFG_STARTUP_GROUP 1062 +#define IDC_CFG_AUTOSTART 1063 +#define IDC_CFG_AUTOIMPORT 1064 +#define IDC_CFG_AUTOINIT 1065 +#define IDC_CFG_OTHER 1066 +#define IDC_CFG_MONITOR 1069 +#define IDC_CFG_STICKY 1070 +#define IDC_CFG_IDENTS 1071 +#define IDC_CFG_IDENTITY 1072 +#define IDC_CFG_RENEW 1075 +#define IDC_CFG_REMOVE 1076 +#define IDC_CFG_TAB 1077 +#define IDC_CFG_TARGET 1078 +#define IDC_CFG_PLUGINS 1079 +#define IDC_CFG_PLUGINGRP 1080 +#define IDC_CFG_LBL_DESC 1083 +#define IDC_CFG_DESC 1084 +#define IDC_CFG_LBL_STATE 1085 +#define IDC_CFG_STATE 1086 +#define IDC_CFG_LBL_DEPS 1087 +#define IDC_CFG_DEPS 1088 +#define IDC_CFG_DISABLE 1089 +#define IDC_CFG_ENABLE 1090 +#define IDC_CFG_PROVGRP 1091 +#define IDC_CFG_LBL_MOD 1092 +#define IDC_CFG_MODULE 1093 +#define IDC_CFG_LBL_VEN 1094 +#define IDC_CFG_VENDOR 1095 +#define IDC_CFG_REGISTER 1097 +#define IDC_CFG_NETDETECT 1098 +#define IDC_PP_STICKY 1099 +#define IDC_PRODUCT 1100 +#define IDC_COPYRIGHT 1101 +#define IDC_BUILDINFO 1102 +#define IDC_LIST1 1103 +#define IDC_MODULES 1103 +#define IDA_ACTIVATE_MENU 40003 +#define IDA_UP 40004 +#define IDA_DOWN 40005 +#define IDA_LEFT 40006 +#define IDA_RIGHT 40007 +#define IDA_ESC 40008 +#define IDA_ENTER 40009 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 198 +#define _APS_NEXT_COMMAND_VALUE 40010 +#define _APS_NEXT_CONTROL_VALUE 1104 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/windows/identity/ui/statusbar.c b/src/windows/identity/ui/statusbar.c new file mode 100644 index 000000000..75f520c57 --- /dev/null +++ b/src/windows/identity/ui/statusbar.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khmapp.h> + +khui_statusbar_part khui_statusbar_parts[] = { + {KHUI_SBPART_INFO, 0, KHUI_SB_WTYPE_FILLER}, + {KHUI_SBPART_NOTICE, 40, KHUI_SB_WTYPE_RELATIVE}, + {KHUI_SBPART_LOC, 40, KHUI_SB_WTYPE_ABSOLUTE} +}; + +int khui_n_statusbar_parts = sizeof(khui_statusbar_parts) / sizeof(khui_statusbar_part); + +HWND khui_hwnd_statusbar = NULL; + +void khui_statusbar_set_parts(HWND parent) { + int i; + int fillerwidth; + int staticwidth; + int lastx; + int width; + RECT r; + INT * parts; + + GetClientRect(parent, &r); + width = r.right - r.left; + + /* calculate fillerwidth and staticwidth */ + staticwidth = 0; + for(i=0;i<khui_n_statusbar_parts;i++) { + if(khui_statusbar_parts[i].wtype == KHUI_SB_WTYPE_ABSOLUTE) { + staticwidth += khui_statusbar_parts[i].width; + } else if(khui_statusbar_parts[i].wtype == KHUI_SB_WTYPE_RELATIVE) { + staticwidth += (khui_statusbar_parts[i].width * width) / 100; + } + } + + fillerwidth = width - staticwidth; + + parts = malloc(sizeof(INT) * khui_n_statusbar_parts); + + lastx = 0; + for(i=0;i<khui_n_statusbar_parts;i++) { + int w; + switch(khui_statusbar_parts[i].wtype) { + case KHUI_SB_WTYPE_ABSOLUTE: + w = khui_statusbar_parts[i].width; + break; + + case KHUI_SB_WTYPE_RELATIVE: + w = (khui_statusbar_parts[i].width * width) / 100; + break; + + case KHUI_SB_WTYPE_FILLER: + w = fillerwidth; + break; + } + lastx += w; + + if(i==khui_n_statusbar_parts - 1) + parts[i] = -1; + else + parts[i] = lastx; + } + + SendMessage( + khui_hwnd_statusbar, + SB_SETPARTS, + khui_n_statusbar_parts, + (LPARAM) parts); + + free(parts); +} + +void khui_create_statusbar(HWND parent) { + HWND hwsb; + + hwsb = CreateWindowEx( + 0, + STATUSCLASSNAME, + NULL, + SBARS_SIZEGRIP | WS_CHILD | WS_VISIBLE, + 0,0,0,0, + parent, + NULL, + khm_hInstance, + NULL); + + if(!hwsb) + return; + + khui_hwnd_statusbar = hwsb; + + khui_statusbar_set_parts(parent); +} + +void khui_update_statusbar(HWND parent) { + MoveWindow(khui_hwnd_statusbar, 0, 0, 0, 0, TRUE); + khui_statusbar_set_parts(parent); +} + +int sb_find_index(int id) { + int i; + + for(i=0;i<khui_n_statusbar_parts;i++) { + if(khui_statusbar_parts[i].id == id) + return i; + } + + return -1; +} + +void khui_statusbar_set_text(int id, wchar_t * text) { + int idx; + + idx = sb_find_index(id); + if(idx < 0) + return; + + SendMessage( + khui_hwnd_statusbar, + SB_SETTEXT, + idx, + (LPARAM) text); +} + diff --git a/src/windows/identity/ui/statusbar.h b/src/windows/identity/ui/statusbar.h new file mode 100644 index 000000000..b4f2a6e30 --- /dev/null +++ b/src/windows/identity/ui/statusbar.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_STATUSBAR_H +#define __KHIMAIRA_STATUSBAR_H + +typedef struct khui_statusbar_part_t { + int id; + int width; + int wtype; /* one of KHUI_SB_WTYPE_* */ +} khui_statusbar_part; + +#define KHUI_SB_WTYPE_RELATIVE 1 +#define KHUI_SB_WTYPE_ABSOLUTE 2 +#define KHUI_SB_WTYPE_FILLER 4 + +/* statusbar parts */ +#define KHUI_SBPART_INFO 1 +#define KHUI_SBPART_NOTICE 2 +#define KHUI_SBPART_LOC 3 + +extern HWND khui_hwnd_statusbar; +extern khui_statusbar_part khui_statusbar_parts[]; +extern int khui_n_statusbar_parts; + +void khui_create_statusbar(HWND p); +void khui_update_statusbar(HWND parent); +void khui_statusbar_set_text(int id, wchar_t * text); + +#endif \ No newline at end of file diff --git a/src/windows/identity/ui/timer.c b/src/windows/identity/ui/timer.c new file mode 100644 index 000000000..bd5b30eff --- /dev/null +++ b/src/windows/identity/ui/timer.c @@ -0,0 +1,637 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khmapp.h> +#include<assert.h> + +/* in seconds */ +#if 0 +khm_int32 khui_timeout_warn = KHUI_DEF_TIMEOUT_WARN; +khm_int32 khui_timeout_crit = KHUI_DEF_TIMEOUT_CRIT; +khm_int32 khui_timeout_renew = KHUI_DEF_TIMEOUT_RENEW; + +khm_boolean khui_do_renew = TRUE; +khm_boolean khui_do_warn = TRUE; +khm_boolean khui_do_crit = TRUE; +#endif + +khui_timer_event * khui_timers = NULL; +khm_size khui_n_timers = 0; +khm_size khui_nc_timers = 0; + +CRITICAL_SECTION cs_timers; + +/********************************************************************* + Timers + *********************************************************************/ + + +#define KHUI_TIMER_ALLOC_INCR 16 + +void +khm_timer_init(void) { +#ifdef DEBUG + assert(khui_timers == NULL); +#endif + + khui_nc_timers = KHUI_TIMER_ALLOC_INCR; + khui_n_timers = 0; + khui_timers = malloc(sizeof(*khui_timers) * khui_nc_timers); + +#ifdef DEBUG + assert(khui_timers != NULL); +#endif + + InitializeCriticalSection(&cs_timers); +} + +void +khm_timer_exit(void) { + EnterCriticalSection(&cs_timers); + + if (khui_timers) + free(khui_timers); + khui_timers = NULL; + khui_n_timers = 0; + khui_nc_timers = 0; + + LeaveCriticalSection(&cs_timers); + DeleteCriticalSection(&cs_timers); +} + +/* called with cs_timers held */ +static void +tmr_fire_timer(void) { + int i; + __int64 curtime; + __int64 err; + __int64 next_event; + int tmr_count[KHUI_N_TTYPES]; + __int64 tmr_offset[KHUI_N_TTYPES]; + int t; + khm_handle eff_ident = NULL; + khui_timer_type eff_type = 0; /* meaningless */ + int fire_count = 0; + + TimetToFileTimeInterval(KHUI_TIMEEQ_ERROR_SMALL, + (LPFILETIME) &err); + GetSystemTimeAsFileTime((LPFILETIME) &curtime); + next_event = 0; + + ZeroMemory(tmr_count, sizeof(tmr_count)); + ZeroMemory(tmr_offset, sizeof(tmr_offset)); + + for (i=0; i < (int) khui_n_timers; i++) { + if (!(khui_timers[i].flags & + (KHUI_TE_FLAG_STALE | KHUI_TE_FLAG_EXPIRED)) && + khui_timers[i].type != KHUI_TTYPE_ID_MARK && + khui_timers[i].expire < curtime + err) { + + t = khui_timers[i].type; + + switch(t) { + case KHUI_TTYPE_ID_RENEW: + khm_cred_renew_identity(khui_timers[i].key); + khui_timers[i].flags |= KHUI_TE_FLAG_EXPIRED; + break; + + case KHUI_TTYPE_CRED_RENEW: + /* the equivalence threshold for setting the timer is + a lot larger than what we are testing for here + (KHUI_TIMEEQ_ERROR vs KHUI_TIMEEQ_ERROR_SMALL) so + we assume that it is safe to trigger a renew_cred + call here without checking if there's an imminent + renew_identity call. */ + khm_cred_renew_cred(khui_timers[i].key); + khui_timers[i].flags |= KHUI_TE_FLAG_EXPIRED; + break; + + default: + if (t < KHUI_N_TTYPES) { + tmr_count[t]++; + if (tmr_offset[t] == 0 || + tmr_offset[t] > khui_timers[i].offset) + tmr_offset[t] = khui_timers[i].offset; + if (next_event == 0 || + next_event > + khui_timers[i].expire + khui_timers[i].offset) + next_event = khui_timers[i].expire + + khui_timers[i].offset; + + if (eff_ident == NULL && + (t == KHUI_TTYPE_ID_EXP || + t == KHUI_TTYPE_ID_CRIT || + t == KHUI_TTYPE_ID_WARN)) { + /* we don't need a hold since we will be done + with the handle before the marker is + expired (the marker is the timer with the + KHUI_TTYPE_ID_MARK which contains a held + handle and is not really a timer.) */ + eff_ident = khui_timers[i].key; + eff_type = t; + } + + fire_count++; + + khui_timers[i].flags |= KHUI_TE_FLAG_EXPIRED; + } +#ifdef DEBUG + else { + assert(FALSE); + } +#endif + } + } + } + + /* See if we have anything to do */ + if (next_event == 0) + return; + else { + wchar_t fmt[128]; + wchar_t wtime[128]; + wchar_t wmsg[256]; + wchar_t wtitle[64]; + __int64 ft_second; + khui_alert * alert = NULL; + + khm_size cb; + + next_event -= curtime; + + /* Due to measurement errors we may be slightly off on our + next_event calculation which shows up as '4 mins 59 + seconds' instead of '5 mins' and so on when converting to a + string. So we add half a second to make the message + neater. */ + TimetToFileTimeInterval(1, (LPFILETIME) &ft_second); + next_event += ft_second / 2; + + cb = sizeof(wtime); + + FtIntervalToString((LPFILETIME) &next_event, + wtime, + &cb); + + if (fire_count == 1 && + eff_ident != NULL && + (eff_type == KHUI_TTYPE_ID_EXP || + eff_type == KHUI_TTYPE_ID_CRIT || + eff_type == KHUI_TTYPE_ID_WARN)) { + + wchar_t idname[KCDB_IDENT_MAXCCH_NAME]; + + cb = sizeof(idname); + kcdb_identity_get_name(eff_ident, idname, &cb); + + if (next_event < ft_second) { + LoadString(khm_hInstance, IDS_WARN_EXPIRED_ID, + fmt, ARRAYLENGTH(fmt)); + + StringCbPrintf(wmsg, sizeof(wmsg), fmt, idname); + } else { + LoadString(khm_hInstance, IDS_WARN_EXPIRE_ID, + fmt, ARRAYLENGTH(fmt)); + + StringCbPrintf(wmsg, sizeof(wmsg), fmt, idname, wtime); + } + } else { + if (next_event < ft_second) { + LoadString(khm_hInstance, IDS_WARN_EXPIRED, + wmsg, ARRAYLENGTH(wmsg)); + } else { + LoadString(khm_hInstance, IDS_WARN_EXPIRE, + fmt, ARRAYLENGTH(fmt)); + + StringCbPrintf(wmsg, sizeof(wmsg), fmt, wtime); + } + } + + LoadString(khm_hInstance, IDS_WARN_TITLE, + wtitle, ARRAYLENGTH(wtitle)); + + khui_alert_create_simple(wtitle, wmsg, KHERR_WARNING, &alert); + khui_alert_set_flags(alert, KHUI_ALERT_FLAG_REQUEST_BALLOON, + KHUI_ALERT_FLAG_REQUEST_BALLOON); + khui_alert_show(alert); + khui_alert_release(alert); + } +} + +void +khm_timer_fire(HWND hwnd) { + EnterCriticalSection(&cs_timers); + tmr_fire_timer(); + LeaveCriticalSection(&cs_timers); + + khm_timer_refresh(hwnd); +} + +static int +tmr_update(khm_handle key, khui_timer_type type, __int64 expire, + __int64 offset, void * data) { + int i; + + for (i=0; i < (int) khui_n_timers; i++) { + if (khui_timers[i].key == key && + khui_timers[i].type == type) + break; + } + + if (i >= (int) khui_n_timers) { + i = (int) khui_n_timers; + + if (i >= (int) khui_nc_timers) { + khui_timer_event * nt; +#ifdef DEBUG + assert(khui_timers); +#endif + khui_nc_timers = UBOUNDSS(i+1, KHUI_TIMER_ALLOC_INCR, + KHUI_TIMER_ALLOC_INCR); + nt = malloc(sizeof(*nt) * khui_nc_timers); +#ifdef DEBUG + assert(nt); +#endif + memcpy(nt, khui_timers, sizeof(*nt) * khui_n_timers); + + free(khui_timers); + khui_timers = nt; + } + + khui_timers[i].key = key; + khui_timers[i].type = type; + khui_timers[i].flags = 0; + khui_n_timers++; + } + + khui_timers[i].expire = expire; + khui_timers[i].offset = offset; + khui_timers[i].data = data; + + khui_timers[i].flags &= ~KHUI_TE_FLAG_STALE; + + return i; +} + +/* called with cs_timers held */ +static int +tmr_find(khm_handle key, khui_timer_type type, + khm_int32 and_flags, khm_int32 eq_flags) { + int i; + + eq_flags &= and_flags; + + for (i=0; i < (int) khui_n_timers; i++) { + if (khui_timers[i].key == key && + khui_timers[i].type == type && + (khui_timers[i].flags & and_flags) == eq_flags) + break; + } + + if (i < (int) khui_n_timers) + return i; + else + return -1; +} + +/* called with cs_timers held */ +static khm_int32 KHMAPI +tmr_cred_apply_proc(khm_handle cred, void * rock) { + khm_handle ident = NULL; + int mark_idx; + int idx; + __int64 ft_expiry; + __int64 ft_current; + __int64 ft_cred_expiry; + __int64 ft; + __int64 fte; + khm_size cb; + + kcdb_cred_get_identity(cred, &ident); +#ifdef DEBUG + assert(ident); +#endif + + /* now get the expiry */ + cb = sizeof(ft_expiry); + if (KHM_FAILED(kcdb_identity_get_attr(ident, KCDB_ATTR_EXPIRE, + NULL, + &ft_expiry, &cb))) + if (KHM_FAILED(kcdb_cred_get_attr(cred, KCDB_ATTR_EXPIRE, + NULL, + &ft_expiry, &cb))) { + /* we don't have an expiry time to work with */ + kcdb_identity_release(ident); + return KHM_ERROR_SUCCESS; + } + + /* and the current time */ + GetSystemTimeAsFileTime((LPFILETIME) &ft_current); + + mark_idx = tmr_find(ident, KHUI_TTYPE_ID_MARK, 0, 0); + + if (mark_idx < 0) { + mark_idx = tmr_update(ident, KHUI_TTYPE_ID_MARK, 0, 0, 0); + kcdb_identity_hold(ident); +#ifdef DEBUG + assert(mark_idx >= 0); +#endif + khui_timers[mark_idx].flags |= KHUI_TE_FLAG_STALE; + } + + if (khui_timers[mark_idx].flags & KHUI_TE_FLAG_STALE) { + /* first time we are touching this */ + khm_handle csp_cw = NULL; + khm_handle csp_id = NULL; + khm_int32 rv; + khm_int32 t; + khm_boolean do_warn = TRUE; + khm_boolean do_crit = TRUE; + khm_boolean do_renew = TRUE; + khm_boolean renew_done = FALSE; + khm_boolean monitor = TRUE; + khm_int32 to_warn = KHUI_DEF_TIMEOUT_WARN; + khm_int32 to_crit = KHUI_DEF_TIMEOUT_CRIT; + khm_int32 to_renew = KHUI_DEF_TIMEOUT_RENEW; + + if (ft_expiry < ft_current) + /* already expired */ + goto _done_with_ident; + + rv = khc_open_space(NULL, L"CredWindow", KHM_PERM_READ, + &csp_cw); + + assert(KHM_SUCCEEDED(rv)); + + rv = kcdb_identity_get_config(ident, KHM_PERM_READ, &csp_id); + if (KHM_SUCCEEDED(rv)) { + khc_shadow_space(csp_id, csp_cw); + khc_close_space(csp_cw); + } else { + csp_id = csp_cw; + } + csp_cw = NULL; + + rv = khc_read_int32(csp_id, L"Monitor", &t); + if (KHM_SUCCEEDED(rv)) + monitor = t; + + rv = khc_read_int32(csp_id, L"AllowWarn", &t); + if (KHM_SUCCEEDED(rv)) + do_warn = t; + + rv = khc_read_int32(csp_id, L"AllowCritical", &t); + if (KHM_SUCCEEDED(rv)) + do_crit = t; + + rv = khc_read_int32(csp_id, L"AllowAutoRenew", &t); + if (KHM_SUCCEEDED(rv)) + do_renew = t; + + rv = khc_read_int32(csp_id, L"WarnThreshold", &t); + if (KHM_SUCCEEDED(rv)) + to_warn = t; + + rv = khc_read_int32(csp_id, L"CriticalThreshold", &t); + if (KHM_SUCCEEDED(rv)) + to_crit = t; + + rv = khc_read_int32(csp_id, L"AutoRenewThreshold", &t); + if (KHM_SUCCEEDED(rv)) + to_renew = t; + + khc_close_space(csp_id); + + if (monitor && do_renew) { + TimetToFileTimeInterval(to_renew, (LPFILETIME) &ft); + fte = ft_expiry - ft; + + if (fte > ft_current) { + tmr_update(ident, KHUI_TTYPE_ID_RENEW, fte, ft, 0); + renew_done = TRUE; + } + } + + if (monitor && do_warn && !renew_done) { + TimetToFileTimeInterval(to_warn, (LPFILETIME) &ft); + fte = ft_expiry - ft; + + if (fte > ft_current) + tmr_update(ident, KHUI_TTYPE_ID_WARN, fte, ft, 0); + } + + if (monitor && do_crit && !renew_done) { + TimetToFileTimeInterval(to_crit, (LPFILETIME) &ft); + fte = ft_expiry - ft; + + if (fte > ft_current) + tmr_update(ident, KHUI_TTYPE_ID_CRIT, fte, ft, 0); + } + + if (monitor && !renew_done) { + if (ft_expiry > ft_current) + tmr_update(ident, KHUI_TTYPE_ID_EXP, ft_expiry, 0, 0); + } + + _done_with_ident: + khui_timers[mark_idx].flags &= ~KHUI_TE_FLAG_STALE; + } + + cb = sizeof(ft_cred_expiry); + if (KHM_FAILED(kcdb_cred_get_attr(cred, KCDB_ATTR_EXPIRE, + NULL, + &ft_cred_expiry, + &cb))) + goto _cleanup; + + TimetToFileTimeInterval(KHUI_TIMEEQ_ERROR, (LPFILETIME) &ft); + + if (ft_cred_expiry >= ft_expiry || + (ft_expiry - ft_cred_expiry) < ft) + goto _cleanup; + + if ((idx = tmr_find(ident, KHUI_TTYPE_ID_WARN, 0, 0)) >= 0 && + !(khui_timers[idx].flags & KHUI_TE_FLAG_STALE)) { + + fte = ft_cred_expiry - khui_timers[idx].offset; + if (fte > ft_current) { + tmr_update(cred, KHUI_TTYPE_CRED_WARN, fte, + khui_timers[idx].offset, 0); + kcdb_cred_hold(cred); + } + } + + if ((idx = tmr_find(ident, KHUI_TTYPE_ID_CRIT, 0, 0)) >= 0 && + !(khui_timers[idx].flags & KHUI_TE_FLAG_STALE)) { + + fte = ft_cred_expiry - khui_timers[idx].offset; + if (fte > ft_current) { + tmr_update(cred, KHUI_TTYPE_CRED_CRIT, fte, + khui_timers[idx].offset, 0); + kcdb_cred_hold(cred); + } + } + + if ((idx = tmr_find(ident, KHUI_TTYPE_ID_RENEW, 0, 0)) >= 0 && + !(khui_timers[idx].flags & KHUI_TE_FLAG_STALE)) { + + fte = ft_cred_expiry - khui_timers[idx].offset; + if (fte > ft_current) { + tmr_update(cred, KHUI_TTYPE_CRED_RENEW, fte, + khui_timers[idx].offset, 0); + kcdb_cred_hold(cred); + } + } + + if ((idx = tmr_find(ident, KHUI_TTYPE_ID_EXP, 0, 0)) >= 0 && + !(khui_timers[idx].flags & KHUI_TE_FLAG_STALE)) { + + if (ft_cred_expiry > ft_current) { + tmr_update(cred, KHUI_TTYPE_CRED_EXP, ft_cred_expiry, + 0, 0); + } + } + + _cleanup: + + if (ident) + kcdb_identity_release(ident); + + return KHM_ERROR_SUCCESS; +} + +/* called with cs_timers held */ +static void +tmr_purge(void) { + int i, j; + + for (i=0,j=0; i < (int) khui_n_timers; i++) { + if (khui_timers[i].flags & KHUI_TE_FLAG_STALE) { + if (khui_timers[i].type == KHUI_TTYPE_ID_MARK) { + kcdb_identity_release(khui_timers[i].key); +#ifdef DEBUG + { + int idx; + + idx = tmr_find(khui_timers[i].key, + KHUI_TTYPE_ID_CRIT, 0, 0); + assert(idx < 0 || + (khui_timers[idx].flags & + KHUI_TE_FLAG_STALE)); + + idx = tmr_find(khui_timers[i].key, + KHUI_TTYPE_ID_RENEW, 0, 0); + assert(idx < 0 || + (khui_timers[idx].flags & + KHUI_TE_FLAG_STALE)); + + idx = tmr_find(khui_timers[i].key, + KHUI_TTYPE_ID_WARN, 0, 0); + assert(idx < 0 || + (khui_timers[idx].flags & + KHUI_TE_FLAG_STALE)); + + idx = tmr_find(khui_timers[i].key, + KHUI_TTYPE_ID_EXP, 0, 0); + assert(idx < 0 || + (khui_timers[idx].flags & + KHUI_TE_FLAG_STALE)); + } +#endif + } else if (khui_timers[i].type == KHUI_TTYPE_CRED_WARN || + khui_timers[i].type == KHUI_TTYPE_CRED_CRIT || + khui_timers[i].type == KHUI_TTYPE_CRED_RENEW || + khui_timers[i].type == KHUI_TTYPE_CRED_EXP) { + kcdb_cred_release(khui_timers[i].key); + } + } else { + if (i != j) + khui_timers[j] = khui_timers[i]; + j++; + } + } + + khui_n_timers = j; +} + +void +khm_timer_refresh(HWND hwnd) { + int i; + __int64 next_event = 0; + __int64 curtime; + __int64 diff; + + EnterCriticalSection(&cs_timers); + + KillTimer(hwnd, KHUI_TRIGGER_TIMER_ID); + + for (i=0; i < (int) khui_n_timers; i++) { +#ifdef NOT_IMPLEMENTED_YET + if (khui_timers[i].type == KHUI_TTYPE_BMSG || + khui_timers[i].type == KHUI_TTYPE_SMSG) { + khui_timers[i].flags &= ~KHUI_TE_FLAG_STALE; + } else +#endif + khui_timers[i].flags |= KHUI_TE_FLAG_STALE; + } + + kcdb_credset_apply(NULL, + tmr_cred_apply_proc, + NULL); + + tmr_purge(); + + _check_next_event: + + next_event = 0; + for (i=0; i < (int) khui_n_timers; i++) { + if (next_event == 0 || + (!(khui_timers[i].flags & KHUI_TE_FLAG_EXPIRED) && + khui_timers[i].type != KHUI_TTYPE_ID_MARK && + next_event > khui_timers[i].expire)) + next_event = khui_timers[i].expire; + } + + if (next_event != 0) { + GetSystemTimeAsFileTime((LPFILETIME) &curtime); + + TimetToFileTimeInterval(KHUI_TIMEEQ_ERROR_SMALL, + (LPFILETIME) &diff); + + if (curtime + diff > next_event) { + tmr_fire_timer(); + goto _check_next_event; + } else { + diff = next_event - curtime; + SetTimer(hwnd, + KHUI_TRIGGER_TIMER_ID, + FtIntervalToMilliseconds((LPFILETIME) &diff), + NULL); + } + } + + LeaveCriticalSection(&cs_timers); +} diff --git a/src/windows/identity/ui/timer.h b/src/windows/identity/ui/timer.h new file mode 100644 index 000000000..921b9dcc5 --- /dev/null +++ b/src/windows/identity/ui/timer.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_TIMER_H +#define __KHIMAIRA_TIMER_H + +/* note that the ordering of the first few enum constants are + significant. The values of the constants up to KHUI_N_TTYPES are + used as indices. */ +typedef enum tag_khui_timer_type { + KHUI_TTYPE_ID_EXP = 0, /* Identity expiration */ + KHUI_TTYPE_ID_CRIT, /* Identity critical */ + KHUI_TTYPE_ID_WARN, /* Identity warning */ + KHUI_TTYPE_CRED_EXP, /* Credential expiration */ + KHUI_TTYPE_CRED_CRIT, /* Credential critical */ + KHUI_TTYPE_CRED_WARN, /* Credential warning */ + + KHUI_N_TTYPES, /* Count of the timers that we + aggregate for notifications */ + + KHUI_TTYPE_ID_MARK, /* Identity marker */ + + KHUI_TTYPE_ID_RENEW, /* Identity auto renewal */ + KHUI_TTYPE_CRED_RENEW, /* Credential renewal */ + +#if 0 + KHUI_TTYPE_BMSG, /* Custom. Sends broadcast message + when triggered.*/ + KHUI_TTYPE_SMSG, /* Custom. Sends subscription message + when triggered. */ +#endif +} khui_timer_type; + +typedef struct tag_khui_timer_event { + khm_handle key; + khui_timer_type type; + + __int64 expire; /* time at which the timer expires */ + __int64 offset; /* time offset at which the event that + the timer warns of happens */ + void * data; + khm_int32 flags; +} khui_timer_event; + +#define KHUI_TRIGGER_TIMER_ID 48 +#define KHUI_REFRESH_TIMER_ID 49 + +#define KHUI_REFRESH_TIMEOUT 5000 + +#define KHUI_TE_FLAG_EXPIRED 0x00000001 +#define KHUI_TE_FLAG_STALE 0x00000002 + +#define KHUI_DEF_TIMEOUT_WARN 900 +#define KHUI_DEF_TIMEOUT_CRIT 300 +#define KHUI_DEF_TIMEOUT_RENEW 60 + +/* the max absolute difference between two timers (in seconds) that + can exist where we consider both timers to be in the same + timeslot. */ +#define KHUI_TIMEEQ_ERROR 20 + +/* the small error. */ +#define KHUI_TIMEEQ_ERROR_SMALL 1 + +void +khm_timer_refresh(HWND hwnd); + +void +khm_timer_fire(HWND hwnd); + +void +khm_timer_init(void); + +void +khm_timer_exit(void); + +#endif diff --git a/src/windows/identity/ui/toolbar.c b/src/windows/identity/ui/toolbar.c new file mode 100644 index 000000000..d1a84e235 --- /dev/null +++ b/src/windows/identity/ui/toolbar.c @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khmapp.h> +#include<assert.h> + +HWND khui_hwnd_standard_toolbar; +int khui_tb_blank; + +khui_ilist * ilist_toolbar; + +void khui_init_toolbar(void) { + ilist_toolbar = khui_create_ilist(KHUI_TOOLBAR_IMAGE_WIDTH, KHUI_TOOLBAR_IMAGE_HEIGHT, KHUI_TOOLBAR_MAX_BTNS, 5, 0); +} + +void khui_exit_toolbar(void) { + khui_delete_ilist(ilist_toolbar); +} + +LRESULT khm_toolbar_notify(LPNMHDR notice) { + switch(notice->code) { + case NM_CUSTOMDRAW: + { + LPNMTBCUSTOMDRAW nmcd = (LPNMTBCUSTOMDRAW) notice; + if(nmcd->nmcd.dwDrawStage == CDDS_PREPAINT) { + return CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTERASE; + } else if(nmcd->nmcd.dwDrawStage == CDDS_ITEMPREPAINT) { + return CDRF_NOTIFYPOSTPAINT; + } else if(nmcd->nmcd.dwDrawStage == CDDS_ITEMPOSTPAINT) { + /* draw the actual icon */ + int iidx; + int ibmp; + HBITMAP hbmp; + RECT r; + + khui_action * act = + khui_find_action((int) nmcd->nmcd.dwItemSpec); + + if(!act || !act->ib_normal) + return CDRF_DODEFAULT; + + if((act->state & KHUI_ACTIONSTATE_DISABLED) && + act->ib_disabled) { + ibmp = act->ib_disabled; + } else if(act->ib_hot && + ((nmcd->nmcd.uItemState & CDIS_HOT) || + (nmcd->nmcd.uItemState & CDIS_SELECTED))){ + ibmp = act->ib_hot; + } else { + ibmp = act->ib_normal; + } + + iidx = khui_ilist_lookup_id(ilist_toolbar, ibmp); + if(iidx < 0) { + hbmp = LoadImage(khm_hInstance, + MAKEINTRESOURCE(ibmp), + IMAGE_BITMAP, + KHUI_TOOLBAR_IMAGE_WIDTH, + KHUI_TOOLBAR_IMAGE_HEIGHT, 0); + iidx = + khui_ilist_add_masked_id(ilist_toolbar, + hbmp, + KHUI_TOOLBAR_BGCOLOR, + ibmp); + DeleteObject(hbmp); + } + + if(iidx < 0) + return CDRF_DODEFAULT; + + CopyRect(&r, &(nmcd->nmcd.rc)); + r.left += ((r.right - r.left) - + KHUI_TOOLBAR_IMAGE_WIDTH) / 2; + r.top += ((r.bottom - r.top) - + KHUI_TOOLBAR_IMAGE_HEIGHT) / 2; + + khui_ilist_draw(ilist_toolbar, + iidx, + nmcd->nmcd.hdc, + r.left, + r.top, + 0); + + return CDRF_DODEFAULT; + } + } + break; + } + return 0; +} + +void khui_add_action_to_toolbar(HWND tb, khui_action *a, int opt, HIMAGELIST hiList) { + wchar_t buf[MAX_RES_STRING] = L""; + int idx_caption = 0; + TBBUTTON bn; + LRESULT lr; + + ZeroMemory(&bn,sizeof(bn)); + + if(opt & KHUI_TOOLBAR_ADD_SEP) { + bn.fsStyle = BTNS_SEP; + bn.iBitmap = 3; + + lr = SendMessage( + tb, + TB_ADDBUTTONS, + 1, + (LPARAM) &bn); +#ifdef DEBUG + assert(lr); +#endif + return; + } + + bn.fsStyle = BTNS_BUTTON; + + if(opt & KHUI_TOOLBAR_VARSIZE) { + bn.fsStyle |= BTNS_AUTOSIZE; + } + + if(opt & KHUI_TOOLBAR_ADD_TEXT) { + int sid = 0; + if((opt & KHUI_TOOLBAR_ADD_LONGTEXT) == + KHUI_TOOLBAR_ADD_LONGTEXT) { + sid = a->is_tooltip; + } + if(!sid) + sid = a->is_caption; + if(sid) { + LoadString(khm_hInstance, + sid, + buf, ARRAYLENGTH(buf)); + buf[wcslen(buf) + 1] = L'\0'; + idx_caption = (int) SendMessage(tb, + TB_ADDSTRING, + (WPARAM) NULL, + (LPARAM) buf); + bn.fsStyle |= BTNS_SHOWTEXT; + bn.iString = idx_caption; + } + } + + if(opt & KHUI_TOOLBAR_ADD_DROPDOWN) { + bn.fsStyle |= BTNS_DROPDOWN; + } + + if((opt & KHUI_TOOLBAR_ADD_BITMAP) && a->ib_normal) { + bn.fsStyle |= TBSTYLE_CUSTOMERASE; + bn.iBitmap = khui_tb_blank; + } else + bn.iBitmap = I_IMAGENONE; + + bn.idCommand = a->cmd; + + if(a->state & KHUI_ACTIONSTATE_DISABLED) { + bn.fsState = 0; + } else { + bn.fsState = TBSTATE_ENABLED; + } + + if(a->state & KHUI_ACTIONSTATE_CHECKED) { + bn.fsState |= TBSTATE_CHECKED; + } + + bn.dwData = 0; + + lr = SendMessage( + tb, + TB_ADDBUTTONS, + 1, + (LPARAM) &bn); + +#ifdef DEBUG + assert(lr); +#endif +} + +void khm_update_standard_toolbar(void) +{ + khui_menu_def * def; + khui_action_ref * aref; + khui_action * act; + + def = khui_find_menu(KHUI_TOOLBAR_STANDARD); + + aref = def->items; + + while(aref && aref->action != KHUI_MENU_END) { + if(aref->action == KHUI_MENU_SEP) { + aref++; + continue; + } + + act = khui_find_action(aref->action); + if(act) { + BOOL enable; + + enable = !(act->state & KHUI_ACTIONSTATE_DISABLED); + SendMessage(khui_hwnd_standard_toolbar, + TB_ENABLEBUTTON, + (WPARAM) act->cmd, + MAKELPARAM(enable, 0)); + } + + aref++; + } +} + +void khm_create_standard_toolbar(HWND rebar) { + HWND hwtb; + SIZE sz; + HBITMAP hbm_blank; + HIMAGELIST hiList; + REBARBANDINFO rbi; + khui_menu_def * def; + khui_action * act; + khui_action_ref * aref; + int idx_blank; + + def = khui_find_menu(KHUI_TOOLBAR_STANDARD); + + hwtb = CreateWindowEx( + TBSTYLE_EX_MIXEDBUTTONS, + TOOLBARCLASSNAME, + (LPWSTR) NULL, + WS_CHILD | + TBSTYLE_FLAT | + TBSTYLE_AUTOSIZE | + TBSTYLE_LIST | + CCS_NORESIZE | + CCS_NOPARENTALIGN | + CCS_ADJUSTABLE | + CCS_NODIVIDER, + 0, 0, 0, 0, rebar, + (HMENU) NULL, khm_hInstance, + NULL); + + if(!hwtb) { +#ifdef DEBUG + assert(FALSE); +#else + return; +#endif + } + + hiList = ImageList_Create( + KHUI_TOOLBAR_IMAGE_WIDTH, + KHUI_TOOLBAR_IMAGE_HEIGHT, + ILC_MASK, + (int) khui_action_list_length(def->items), + 3); + + hbm_blank = LoadImage(khm_hInstance, + MAKEINTRESOURCE(IDB_TB_BLANK), + IMAGE_BITMAP, + KHUI_TOOLBAR_IMAGE_WIDTH, + KHUI_TOOLBAR_IMAGE_HEIGHT, 0); + idx_blank = ImageList_AddMasked(hiList, hbm_blank, RGB(0,0,0)); + + khui_hwnd_standard_toolbar = hwtb; + khui_tb_blank = idx_blank; + + def = khui_find_menu(KHUI_TOOLBAR_STANDARD); + + aref = def->items; + + SendMessage(hwtb, + TB_BUTTONSTRUCTSIZE, + sizeof(TBBUTTON), + 0); + + SendMessage(hwtb, + TB_SETBITMAPSIZE, + 0, + MAKELONG(KHUI_TOOLBAR_IMAGE_WIDTH,KHUI_TOOLBAR_IMAGE_HEIGHT)); + + SendMessage(hwtb, + TB_SETIMAGELIST, + 0, + (LPARAM) hiList); + + SendMessage(hwtb, + TB_SETBUTTONSIZE, + 0, + MAKELONG(KHUI_TOOLBAR_IMAGE_WIDTH,KHUI_TOOLBAR_IMAGE_HEIGHT)); + + while(aref && aref->action != KHUI_MENU_END) { + if(aref->action == KHUI_MENU_SEP) { + khui_add_action_to_toolbar(hwtb, + NULL, + KHUI_TOOLBAR_ADD_SEP, + hiList); + } else { + act = khui_find_action(aref->action); + khui_add_action_to_toolbar(hwtb, + act, + KHUI_TOOLBAR_ADD_BITMAP, + hiList); + } + aref ++; + } + + SendMessage(hwtb, + TB_AUTOSIZE, + 0,0); + + SendMessage(hwtb, + TB_GETMAXSIZE, + 0, + (LPARAM) &sz); + + sz.cy += 5; + + ZeroMemory(&rbi, sizeof(rbi)); + + rbi.cbSize = sizeof(rbi); + rbi.fMask = + RBBIM_ID | + RBBIM_CHILD | + RBBIM_CHILDSIZE | + RBBIM_IDEALSIZE | + RBBIM_SIZE | + RBBIM_STYLE; + rbi.fStyle = + RBBS_USECHEVRON | + RBBS_BREAK; + rbi.hwndChild = hwtb; + + rbi.wID = KHUI_TOOLBAR_STANDARD; + rbi.cx = sz.cx; + rbi.cxMinChild = sz.cx; + rbi.cyMinChild = sz.cy; + rbi.cyChild = rbi.cyMinChild; + rbi.cyMaxChild = rbi.cyMinChild; + rbi.cyIntegral = rbi.cyMinChild; + + rbi.cxIdeal = rbi.cx; + + SendMessage(rebar, + RB_INSERTBAND, + 1, + (LPARAM) &rbi); +} diff --git a/src/windows/identity/ui/toolbar.h b/src/windows/identity/ui/toolbar.h new file mode 100644 index 000000000..65598debc --- /dev/null +++ b/src/windows/identity/ui/toolbar.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_TOOLBAR_H +#define __KHIMAIRA_TOOLBAR_H + +extern HWND khui_hwnd_standard_toolbar; + +void khui_init_toolbar(void); +void khui_exit_toolbar(void); +LRESULT khm_toolbar_notify(LPNMHDR notice); +void khm_create_standard_toolbar(HWND rebar); +void khui_add_action_to_toolbar(HWND toolbar, khui_action * act, int opt, HIMAGELIST hiList); +void khm_update_standard_toolbar(void); + +/* options for khui_add_action_to_toolbar */ +#define KHUI_TOOLBAR_ADD_TEXT 1 +#define KHUI_TOOLBAR_ADD_BITMAP 2 +#define KHUI_TOOLBAR_ADD_LONGTEXT 5 +#define KHUI_TOOLBAR_ADD_DROPDOWN 8 +#define KHUI_TOOLBAR_ADD_SEP 16 +#define KHUI_TOOLBAR_VARSIZE 32 + +#define KHUI_TOOLBAR_IMAGE_WIDTH 29 +#define KHUI_TOOLBAR_IMAGE_HEIGHT 27 +#define KHUI_TOOLBAR_BGCOLOR RGB(0xd7,0xd7,0xd7) +#define KHUI_TOOLBAR_MAX_BTNS 64 + +#endif \ No newline at end of file diff --git a/src/windows/identity/ui/uiconfig.csv b/src/windows/identity/ui/uiconfig.csv new file mode 100644 index 000000000..eeb44bbd3 --- /dev/null +++ b/src/windows/identity/ui/uiconfig.csv @@ -0,0 +1,111 @@ +Name,Type,Value,Description +CredWindow,KC_SPACE,0,Options for the credentials window + AutoInit,KC_INT32,0,Prompt for creds if there arent any + AutoStart,KC_INT32,0,Start Khimaira when Windows starts + AutoImport,KC_INT32,0,Import Windows creds when Khimaira starts + AutoDetectNet,KC_INT32,1,Automatically detect network connectivity changes + KeepRunning,KC_INT32,1,Keep running after closing Khimaira + DefaultView,KC_STRING,ByIdentity, + ViewList,KC_STRING,"ByIdentity,ByLocation", + PaddingHorizontal,KC_INT32,4, + PaddingVertical,KC_INT32,2, + PaddingHeader,KC_INT32,16, + Monitor,KC_INT32,1,Monitor credentials + RefreshTimeout,KC_INT32,60,In seconds + WarnThreshold,KC_INT32,900,In seconds + AllowWarn,KC_INT32,1,Boolean. Enables warning. + CriticalThreshold,KC_INT32,300,In seconds + AllowCritical,KC_INT32,1,Boolean. Enables critical. + AutoRenewThreshold,KC_INT32,600,In seconds + AllowAutoRenew,KC_INT32,1,Boolean. + MaxThreshold,KC_INT32,86400,Max value for a threshold (1 day) + MinThreshold,KC_INT32,10,Min value for a threshold (0) + Windows,KC_SPACE,0,Window parameters + _Schema,KC_SPACE,0,Schema + Width,KC_INT32,0, + Height,KC_INT32,0, + XPos,KC_INT32,0, + YPos,KC_INT32,0, + _Schema,KC_ENDSPACE,0, + Main,KC_SPACE,0,Main window + Main,KC_ENDSPACE,0, + Windows,KC_ENDSPACE,0, + Views,KC_SPACE,0,Preconfigured views for credentials + Custom_0,KC_SPACE,0,First custom view. Additional views have names of the form Custom_N + Custom_0,KC_ENDSPACE,0, + ByIdentity,KC_SPACE,0,The default view + Description,KC_STRING,View grouped by identity and credential type, + ColumnList,KC_STRING,"_CWFlags,_CWTypeIcon,IdentityName,TypeName,Name,TimeLeft", + Columns,KC_SPACE,0,Columns + _CWFlags,KC_SPACE,0, + Width,KC_INT32,20, + Flags,KC_INT32,112, + _CWFlags,KC_ENDSPACE,0, + _CWTypeIcon,KC_SPACE,0, + Width,KC_INT32,20, + Flags,KC_INT32,112, + _CWTypeIcon,KC_ENDSPACE,0, + IdentityName,KC_SPACE,0, + Width,KC_INT32,100, + SortIndex,KC_INT32,0, + Flags,KC_INT32,11, + IdentityName,KC_ENDSPACE,0 + TypeName,KC_SPACE,0 + Width,KC_INT32,100 + SortIndex,KC_INT32,1 + Flags,KC_INT32,11 + TypeName,KC_ENDSPACE,0 + Name,KC_SPACE,0 + Width,KC_INT32,200 + SortIndex,KC_INT32,2 + Flags,KC_INT32,3 + Name,KC_ENDSPACE,0 + TimeLeft,KC_SPACE,0 + Width,KC_INT32,200 + Flags,KC_INT32,1 + TimeLeft,KC_ENDSPACE,0 + Columns,KC_ENDSPACE,0 + ByIdentity,KC_ENDSPACE,0 + ByLocation,KC_SPACE,0,View by location + Description,KC_STRING,View grouped by location, + ColumnList,KC_STRING,"_CWFlags,_CWTypeIcon,Location,IdentityName,TypeName,Name,TimeLeft", + Columns,KC_SPACE,0,Columns + _CWFlags,KC_SPACE,0, + Width,KC_INT32,20, + Flags,KC_INT32,112, + _CWFlags,KC_ENDSPACE,0, + _CWTypeIcon,KC_SPACE,0, + Width,KC_INT32,20, + Flags,KC_INT32,112, + _CWTypeIcon,KC_ENDSPACE,0, + Location,KC_SPACE,0, + Width,KC_INT32,100, + SortIndex,KC_INT32,0, + Flags,KC_INT32,11, + Location,KC_ENDSPACE,0, + IdentityName,KC_SPACE,0, + Width,KC_INT32,100, + SortIndex,KC_INT32,1, + Flags,KC_INT32,11, + IdentityName,KC_ENDSPACE,0 + TypeName,KC_SPACE,0 + Width,KC_INT32,100 + SortIndex,KC_INT32,2 + Flags,KC_INT32,11 + TypeName,KC_ENDSPACE,0 + Name,KC_SPACE,0 + Width,KC_INT32,200 + SortIndex,KC_INT32,3 + Flags,KC_INT32,3 + Name,KC_ENDSPACE,0 + TimeLeft,KC_SPACE,0 + Width,KC_INT32,200 + Flags,KC_INT32,1 + TimeLeft,KC_ENDSPACE,0 + Columns,KC_ENDSPACE,0 + ByLocation,KC_ENDSPACE,0 + Views,KC_ENDSPACE,0 + Notices,KC_SPACE,0,Notices and alerts + MinimizeWarning,KC_INT32,1,Show the minimize warning? + Notices,KC_ENDSPACE,0 +CredWindow,KC_ENDSPACE,0 diff --git a/src/windows/identity/uilib/Makefile b/src/windows/identity/uilib/Makefile new file mode 100644 index 000000000..4e6560093 --- /dev/null +++ b/src/windows/identity/uilib/Makefile @@ -0,0 +1,61 @@ +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +MODULE=uilib +!include <../config/Makefile.w32> + +UIDLLOBJFILES= \ + $(OBJ)\rescache.obj \ + $(OBJ)\action.obj \ + $(OBJ)\creddlg.obj \ + $(OBJ)\alert.obj \ + $(OBJ)\propsheet.obj \ + $(OBJ)\propwnd.obj \ + $(OBJ)\uilibmain.obj \ + $(OBJ)\actiondef.obj \ + $(OBJ)\acceldef.obj \ + $(OBJ)\configui.obj \ + $(OBJ)\trackerwnd.obj + +INCFILES= \ + $(INCDIR)\khuidefs.h \ + $(INCDIR)\khrescache.h \ + $(INCDIR)\khaction.h \ + $(INCDIR)\khactiondef.h \ + $(INCDIR)\khalerts.h \ + $(INCDIR)\khhtlink.h \ + $(INCDIR)\khnewcred.h \ + $(INCDIR)\khprops.h \ + $(INCDIR)\khconfigui.h \ + $(INCDIR)\khtracker.h \ + $(INCDIR)\khremote.h + +$(OBJ)\actiondef.c: actions.csv actiondef.cfg + $(CCSV) $** $@ + +$(OBJ)\acceldef.c: accel.csv acceldef.cfg + $(CCSV) $** $@ + +all: mkdirs $(INCFILES) $(UIDLLOBJFILES) + diff --git a/src/windows/identity/uilib/accel.csv b/src/windows/identity/uilib/accel.csv new file mode 100644 index 000000000..80b6ad05f --- /dev/null +++ b/src/windows/identity/uilib/accel.csv @@ -0,0 +1,17 @@ +command,mod,key,scope +KHUI_PACTION_MENU,FVIRTKEY,VK_F10,KHUI_ACCEL_SCOPE_GLOBAL +KHUI_PACTION_UP,FVIRTKEY,VK_UP,KHUI_ACCEL_SCOPE_GLOBAL +KHUI_PACTION_UP_EXTEND,FVIRTKEY|FSHIFT,VK_UP,KHUI_ACCEL_SCOPE_GLOBAL +KHUI_PACTION_UP_TOGGLE,FVIRTKEY|FCONTROL,VK_UP,KHUI_ACCEL_SCOPE_GLOBAL +KHUI_PACTION_DOWN,FVIRTKEY,VK_DOWN,KHUI_ACCEL_SCOPE_GLOBAL +KHUI_PACTION_DOWN_EXTEND,FVIRTKEY|FSHIFT,VK_DOWN,KHUI_ACCEL_SCOPE_GLOBAL +KHUI_PACTION_DOWN_TOGGLE,FVIRTKEY|FCONTROL,VK_DOWN,KHUI_ACCEL_SCOPE_GLOBAL +KHUI_PACTION_LEFT,FVIRTKEY,VK_LEFT,KHUI_ACCEL_SCOPE_GLOBAL +KHUI_PACTION_RIGHT,FVIRTKEY,VK_RIGHT,KHUI_ACCEL_SCOPE_GLOBAL +KHUI_PACTION_ENTER,FVIRTKEY,VK_RETURN,KHUI_ACCEL_SCOPE_GLOBAL +KHUI_PACTION_ESC,FVIRTKEY,VK_ESCAPE,KHUI_ACCEL_SCOPE_GLOBAL +#KHUI_PACTION_DELETE,FVIRTKEY,VK_DELETE,KHUI_ACCEL_SCOPE_GLOBAL +KHUI_ACTION_DESTROY_CRED,FVIRTKEY,VK_DELETE,KHUI_ACCEL_SCOPE_GLOBAL +KHUI_ACTION_EXIT,FCONTROL,\'X\',KHUI_ACCEL_SCOPE_GLOBAL +KHUI_ACTION_VIEW_REFRESH,FVIRTKEY,VK_F5,KHUI_ACCEL_SCOPE_GLOBAL +KHUI_ACTION_NEW_CRED,FCONTROL,\'N\',KHUI_ACCEL_SCOPE_GLOBAL diff --git a/src/windows/identity/uilib/acceldef.cfg b/src/windows/identity/uilib/acceldef.cfg new file mode 100644 index 000000000..5dc72e610 --- /dev/null +++ b/src/windows/identity/uilib/acceldef.cfg @@ -0,0 +1,50 @@ +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, copy, +# modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + +$file_prefix = <<EOS; +/* +This file was autogenerated from src/ui/acceldef.cfg and src/ui/accel.csv. + +Do not modify directly. +*/ +#include<khuidefs.h> + +khui_accel_def khui_accel_global[] = { +EOS + +$record_prefix = "{"; + +$record_sep = ",\n"; + +$record_postfix = "}"; + +$file_postfix = <<EOS; + +}; + +int khui_n_accel_global = sizeof(khui_accel_global) / sizeof(khui_accel_def); + +EOS + +$skip_lines = 1; diff --git a/src/windows/identity/uilib/action.c b/src/windows/identity/uilib/action.c new file mode 100644 index 000000000..cc383c689 --- /dev/null +++ b/src/windows/identity/uilib/action.c @@ -0,0 +1,1019 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#define NOEXPORT +#include<khuidefs.h> +#include<assert.h> + +khui_action_ref khui_main_menu[] = { + MENU_ACTION(KHUI_MENU_FILE), + MENU_ACTION(KHUI_MENU_CRED), + MENU_ACTION(KHUI_MENU_VIEW), + MENU_ACTION(KHUI_MENU_OPTIONS), + MENU_ACTION(KHUI_MENU_HELP), + MENU_END() +}; + +khui_action_ref khui_menu_file[] = { + MENU_ACTION(KHUI_ACTION_PROPERTIES), + MENU_SEP(), + MENU_ACTION(KHUI_ACTION_EXIT), + MENU_END() +}; + +khui_action_ref khui_menu_cred[] = { + MENU_ACTION(KHUI_ACTION_NEW_CRED), + MENU_SEP(), + MENU_ACTION(KHUI_ACTION_RENEW_CRED), + MENU_ACTION(KHUI_ACTION_DESTROY_CRED), + MENU_SEP(), + MENU_ACTION(KHUI_ACTION_SET_DEF_ID), + MENU_ACTION(KHUI_ACTION_SET_SRCH_ID), + MENU_SEP(), + MENU_ACTION(KHUI_ACTION_PASSWD_ID), + MENU_SEP(), + MENU_ACTION(KHUI_ACTION_IMPORT), + MENU_END() +}; + +khui_action_ref khui_menu_layout[] = { + MENU_ACTION(KHUI_ACTION_LAYOUT_ID), + MENU_ACTION(KHUI_ACTION_LAYOUT_TYPE), + MENU_ACTION(KHUI_ACTION_LAYOUT_LOC), + MENU_END() +}; + +khui_action_ref khui_menu_toolbars[] = { + MENU_ACTION(KHUI_ACTION_TB_STANDARD), + MENU_END() +}; + +khui_action_ref khui_menu_view[] = { + MENU_ACTION(KHUI_ACTION_CHOOSE_COLS), + MENU_ACTION(KHUI_MENU_LAYOUT), + MENU_ACTION(KHUI_MENU_TOOLBARS), + MENU_SEP(), + MENU_ACTION(KHUI_ACTION_DEBUG_WINDOW), + MENU_SEP(), + MENU_ACTION(KHUI_ACTION_VIEW_REFRESH), + MENU_END() +}; + +khui_action_ref khui_menu_options[] = { + MENU_ACTION(KHUI_ACTION_OPT_KHIM), + MENU_ACTION(KHUI_ACTION_OPT_IDENTS), + MENU_ACTION(KHUI_ACTION_OPT_NOTIF), + MENU_END() +}; + +khui_action_ref khui_menu_help[] = { + MENU_ACTION(KHUI_ACTION_HELP_CTX), + MENU_SEP(), + MENU_ACTION(KHUI_ACTION_HELP_CONTENTS), + MENU_ACTION(KHUI_ACTION_HELP_INDEX), + MENU_SEP(), + MENU_ACTION(KHUI_ACTION_HELP_ABOUT), + MENU_END() +}; + +khui_action_ref khui_toolbar_standard[] = { + MENU_ACTION(KHUI_ACTION_NEW_CRED), + MENU_ACTION(KHUI_ACTION_RENEW_CRED), + MENU_ACTION(KHUI_ACTION_IMPORT), + MENU_ACTION(KHUI_ACTION_DESTROY_CRED), + MENU_SEP(), + MENU_ACTION(KHUI_ACTION_PASSWD_ID), + MENU_SEP(), + MENU_ACTION(KHUI_ACTION_VIEW_REFRESH), + MENU_ACTION(KHUI_PACTION_BLANK), + MENU_ACTION(KHUI_ACTION_HELP_CTX), + MENU_END() +}; + +khui_action_ref khui_menu_ident_ctx[] = { + MENU_ACTION(KHUI_ACTION_PROPERTIES), + MENU_SEP(), + MENU_ACTION(KHUI_ACTION_SET_DEF_ID), + MENU_ACTION(KHUI_ACTION_SET_SRCH_ID), + MENU_SEP(), + MENU_ACTION(KHUI_ACTION_NEW_CRED), + MENU_ACTION(KHUI_ACTION_RENEW_CRED), + MENU_ACTION(KHUI_ACTION_DESTROY_CRED), + MENU_END() +}; + +khui_action_ref khui_menu_tok_ctx[] = { + MENU_ACTION(KHUI_ACTION_PROPERTIES), + MENU_SEP(), + MENU_ACTION(KHUI_ACTION_NEW_CRED), + MENU_ACTION(KHUI_ACTION_RENEW_CRED), + MENU_ACTION(KHUI_ACTION_DESTROY_CRED), + MENU_END() +}; + +khui_action_ref khui_menu_ico_ctx_min[] = { + MENU_DEFACTION(KHUI_ACTION_OPEN_APP), + MENU_SEP(), + MENU_ACTION(KHUI_ACTION_NEW_CRED), + MENU_ACTION(KHUI_ACTION_RENEW_CRED), + MENU_ACTION(KHUI_ACTION_DESTROY_CRED), + MENU_SEP(), + MENU_ACTION(KHUI_ACTION_EXIT), + MENU_END() +}; + +khui_action_ref khui_menu_ico_ctx_normal[] = { + MENU_DEFACTION(KHUI_ACTION_CLOSE_APP), + MENU_SEP(), + MENU_ACTION(KHUI_ACTION_NEW_CRED), + MENU_ACTION(KHUI_ACTION_RENEW_CRED), + MENU_ACTION(KHUI_ACTION_DESTROY_CRED), + MENU_SEP(), + MENU_ACTION(KHUI_ACTION_EXIT), + MENU_END() +}; + +khui_action_ref khui_pmenu_tok_sel[] = { + MENU_ACTION(KHUI_ACTION_RENEW_CRED), + MENU_ACTION(KHUI_ACTION_DESTROY_CRED), + MENU_END() +}; + +khui_action_ref khui_pmenu_id_sel[] = { + MENU_ACTION(KHUI_ACTION_DESTROY_CRED), + MENU_ACTION(KHUI_ACTION_RENEW_CRED), + MENU_END() +}; + +/* all stock menus and toolbars */ +khui_menu_def khui_all_menus[] = { + CONSTMENU(KHUI_MENU_MAIN, KHUI_MENUSTATE_CONSTANT, khui_main_menu), + CONSTMENU(KHUI_MENU_FILE, KHUI_MENUSTATE_CONSTANT, khui_menu_file), + CONSTMENU(KHUI_MENU_CRED, KHUI_MENUSTATE_CONSTANT, khui_menu_cred), + CONSTMENU(KHUI_MENU_VIEW, KHUI_MENUSTATE_CONSTANT, khui_menu_view), + CONSTMENU(KHUI_MENU_LAYOUT, KHUI_MENUSTATE_CONSTANT, khui_menu_layout), + CONSTMENU(KHUI_MENU_TOOLBARS, KHUI_MENUSTATE_CONSTANT, khui_menu_toolbars), + CONSTMENU(KHUI_MENU_OPTIONS, KHUI_MENUSTATE_CONSTANT, khui_menu_options), + CONSTMENU(KHUI_MENU_HELP, KHUI_MENUSTATE_CONSTANT, khui_menu_help), + + /* toolbars */ + CONSTMENU(KHUI_TOOLBAR_STANDARD, KHUI_MENUSTATE_CONSTANT, khui_toolbar_standard), + + /* context menus */ + CONSTMENU(KHUI_MENU_IDENT_CTX, KHUI_MENUSTATE_CONSTANT, khui_menu_ident_ctx), + CONSTMENU(KHUI_MENU_TOK_CTX, KHUI_MENUSTATE_CONSTANT, khui_menu_tok_ctx), + CONSTMENU(KHUI_MENU_ICO_CTX_MIN, KHUI_MENUSTATE_CONSTANT, khui_menu_ico_ctx_min), + CONSTMENU(KHUI_MENU_ICO_CTX_NORMAL, KHUI_MENUSTATE_CONSTANT, khui_menu_ico_ctx_normal), + + /* pseudo menus */ + CONSTMENU(KHUI_PMENU_TOK_SEL, KHUI_MENUSTATE_CONSTANT, khui_pmenu_tok_sel), + CONSTMENU(KHUI_PMENU_ID_SEL, KHUI_MENUSTATE_CONSTANT, khui_pmenu_id_sel) +}; + +int khui_n_all_menus = sizeof(khui_all_menus) / sizeof(khui_menu_def); +CRITICAL_SECTION cs_actions; + +KHMEXP void KHMAPI +khui_init_actions(void) { + InitializeCriticalSection(&cs_actions); +} + +KHMEXP void KHMAPI +khui_exit_actions(void) { + DeleteCriticalSection(&cs_actions); +} + +#define MENU_NC_ITEMS 8 + +KHMEXP khui_menu_def * KHMAPI +khui_menu_create(int cmd) +{ + khui_menu_def * d; + d = malloc(sizeof(*d)); + ZeroMemory(d, sizeof(*d)); + + d->cmd = cmd; + d->nc_items = MENU_NC_ITEMS; + d->items = malloc(sizeof(*(d->items)) * d->nc_items); + + d->state = KHUI_MENUSTATE_ALLOCD; + + return d; +} + +KHMEXP khui_menu_def * KHMAPI +khui_menu_dup(khui_menu_def * src) +{ + khui_menu_def * d; + size_t i; + size_t n; + + d = khui_menu_create(src->cmd); + + if(src->n_items == -1) + n = khui_action_list_length(src->items); + else + n = src->n_items; + + for(i=0; i<n; i++) { + if(src->items[i].flags & KHUI_ACTIONREF_PACTION) { + khui_menu_add_paction(d, src->items[i].p_action, src->items[i].flags); + } else { + khui_menu_add_action(d, src->items[i].action); + } + } + + return d; +} + +KHMEXP void KHMAPI +khui_menu_delete(khui_menu_def * d) +{ + int i; + + /* non-allocated menus are assumed to have no pointers to other + allocated blocks */ + if(!(d->state & KHUI_MENUSTATE_ALLOCD)) + return; + + for(i=0; i< (int) d->n_items; i++) { + if(d->items[i].flags & KHUI_ACTIONREF_FREE_PACTION) + free(d->items[i].p_action); + } + + if(d->items) + free(d->items); + free(d); +} + +static void khui_menu_assert_size(khui_menu_def * d, size_t n) +{ + if(n > (int) d->nc_items) { + khui_action_ref * ni; + + d->nc_items = UBOUNDSS(n, MENU_NC_ITEMS, MENU_NC_ITEMS); + ni = malloc(sizeof(*(d->items)) * d->nc_items); + memcpy(ni, d->items, sizeof(*(d->items)) * d->n_items); + free(d->items); + d->items = ni; + } +} + +KHMEXP void KHMAPI khui_menu_add_action(khui_menu_def * d, int id) +{ + khui_menu_assert_size(d, d->n_items + 1); + d->items[d->n_items].flags = 0; + d->items[d->n_items ++].action = id; +} + +KHMEXP void KHMAPI khui_menu_add_paction(khui_menu_def * d, khui_action * act, int flags) +{ + khui_menu_assert_size(d, d->n_items + 1); + d->items[d->n_items].flags = flags | KHUI_ACTIONREF_PACTION; + d->items[d->n_items ++].p_action = act; +} + +KHMEXP khui_menu_def * KHMAPI khui_find_menu(int id) { + khui_menu_def * d; + int i; + + d = khui_all_menus; + for(i=0;i<khui_n_all_menus;i++) { + if(id == d[i].cmd) + return &d[i]; + } + + return NULL; +} + +KHMEXP khui_action * KHMAPI khui_find_action(int id) { + khui_action * act; + int i; + + act = khui_actions; + for(i=0;i<khui_n_actions;i++) { + if(act[i].cmd == id) + return &act[i]; + } + + return NULL; +} + +KHMEXP khui_action * KHMAPI khui_find_named_action(wchar_t * name) { + int i; + khui_action * act; + + if(!name) + return NULL; + + act = khui_actions; + for(i=0;i<khui_n_actions;i++) { + if(!act[i].name) + continue; + if(!wcscmp(act[i].name, name)) + return &act[i]; + } + + return NULL; +} + +KHMEXP size_t KHMAPI khui_action_list_length(khui_action_ref * ref) { + size_t c = 0; + while(ref && ref->action != KHUI_MENU_END) { + c++; + ref++; + } + return c; +} + +KHMEXP void KHMAPI khui_check_radio_action(khui_menu_def * d, khm_int32 cmd) +{ + khui_action_ref * r; + khui_action * act; + + r = d->items; + while(r && r->action != KHUI_MENU_END) { + if(r->flags & KHUI_ACTIONREF_PACTION) { + act = r->p_action; + } else { + act = khui_find_action(r->action); + } + + if(act) { + if(act->cmd == cmd) + act->state |= KHUI_ACTIONSTATE_CHECKED; + else + act->state &= ~KHUI_ACTIONSTATE_CHECKED; + } + r++; + } + + kmq_post_message(KMSG_ACT, KMSG_ACT_CHECK, 0, 0); +} + +KHMEXP void KHMAPI khui_check_action(int cmd, khm_boolean check) { + khui_action * act; + + act = khui_find_action(cmd); + if (!act) + return; + + if (check && !(act->state & KHUI_ACTIONSTATE_CHECKED)) + act->state |= KHUI_ACTIONSTATE_CHECKED; + else if (!check && (act->state & KHUI_ACTIONSTATE_CHECKED)) + act->state &= ~KHUI_ACTIONSTATE_CHECKED; + else + return; + + kmq_post_message(KMSG_ACT, KMSG_ACT_CHECK, 0, 0); +} + +KHMEXP void KHMAPI khui_enable_actions(khui_menu_def * d, khm_boolean enable) +{ + khui_action_ref * r; + int delta = FALSE; + khui_action * act; + + r = d->items; + while(r && r->action != KHUI_MENU_END) { + if(r->flags & KHUI_ACTIONREF_PACTION) { + act = r->p_action; + } else { + act = khui_find_action(r->action); + } + + if(act) { + int old_state = act->state; + + if(enable) + act->state &= ~KHUI_ACTIONSTATE_DISABLED; + else + act->state |= KHUI_ACTIONSTATE_DISABLED; + + if(old_state != act->state) + delta = TRUE; + } + r++; + } + + if(delta) { + kmq_send_message(KMSG_ACT, KMSG_ACT_ENABLE, 0, 0); + } +} + +KHMEXP void KHMAPI khui_enable_action(int cmd, khm_boolean enable) { + khui_action * act; + + act = khui_find_action(cmd); + if (!act) + return; + + if (enable && (act->state & KHUI_ACTIONSTATE_DISABLED)) { + act->state &= ~KHUI_ACTIONSTATE_DISABLED; + } else if (!enable && !(act->state & KHUI_ACTIONSTATE_DISABLED)) { + act->state |= KHUI_ACTIONSTATE_DISABLED; + } else + return; + + kmq_send_message(KMSG_ACT, KMSG_ACT_ENABLE, 0, 0); +} + +KHMEXP HACCEL KHMAPI khui_create_global_accel_table(void) { + int i; + ACCEL * accels; + HACCEL ha; + + accels = malloc(sizeof(ACCEL) * khui_n_accel_global); + for(i=0;i<khui_n_accel_global;i++) { + accels[i].cmd = khui_accel_global[i].cmd; + accels[i].fVirt = khui_accel_global[i].mod; + accels[i].key = khui_accel_global[i].key; + } + + ha = CreateAcceleratorTable(accels, khui_n_accel_global); + + free(accels); + + return ha; +} + +KHMEXP khm_boolean KHMAPI +khui_get_cmd_accel_string(int cmd, + wchar_t * buf, + size_t bufsiz) { + int i; + khui_accel_def * def; + + /* should at least hold 2 characters */ + if(bufsiz < sizeof(wchar_t) * 2) + return FALSE; + + buf[0] = L'\0'; + + for(i=0;i<khui_n_accel_global;i++) { + if(khui_accel_global[i].cmd == cmd) + break; + } + + if(i==khui_n_accel_global) + return FALSE; + + def = &khui_accel_global[i]; + + if(def->mod & FALT) { + if(FAILED(StringCbCat(buf, bufsiz, L"ALT+"))) + return FALSE; + } + + + if(def->mod & FCONTROL) { + if(FAILED(StringCbCat(buf, bufsiz, L"CTRL+"))) + return FALSE; + } + + if(def->mod & FSHIFT) { + if(FAILED(StringCbCat(buf, bufsiz, L"SHIFT+"))) + return FALSE; + } + + if(def->mod & FVIRTKEY) { + wchar_t mbuf[6]; + wchar_t * ap = NULL; + switch(def->key) { + case VK_TAB: + ap = L"Tab"; + break; + + case VK_ESCAPE: + ap = L"Esc"; + break; + + case VK_RETURN: + ap = L"Enter"; + break; + + case VK_F5: + ap = L"F5"; + break; + + case VK_DELETE: + ap = L"Del"; + break; + + default: + if((def->key >= '0' && + def->key <= '9') || + (def->key >= 'A' && + def->key <= 'Z')) { + ap = mbuf; + mbuf[0] = (wchar_t) def->key; + mbuf[1] = L'\0'; + } + } + if(ap) { + if(FAILED(StringCbCat(buf, bufsiz, ap))) + return FALSE; + } + else { + if(FAILED(StringCbCat(buf, bufsiz,L"???"))) + return FALSE; + } + + } else { + wchar_t mbuf[2]; + + mbuf[0] = def->key; + mbuf[1] = L'\0'; + + if(FAILED(StringCbCat(buf, bufsiz, mbuf))) + return FALSE; + } + + return TRUE; +} + +/******************************************/ +/* contexts */ + +#define KHUI_ACTION_CONTEXT_MAGIC 0x39c49db5 + +static khm_int32 KHMAPI +khuiint_filter_selected(khm_handle cred, + khm_int32 vflags, + void * rock) { + khm_int32 flags; + if (KHM_SUCCEEDED(kcdb_cred_get_flags(cred, &flags)) && + (flags & KCDB_CRED_FLAG_SELECTED)) + return TRUE; + else + return FALSE; +} + +static void +khuiint_context_release(khui_action_context * ctx) { + ctx->scope = KHUI_SCOPE_NONE; + if (ctx->identity) + kcdb_identity_release(ctx->identity); + ctx->identity = NULL; + ctx->cred_type = KCDB_CREDTYPE_INVALID; + if (ctx->cred) + kcdb_cred_release(ctx->cred); + ctx->cred = NULL; + ctx->n_headers = 0; + if (ctx->credset) + kcdb_credset_flush(ctx->credset); + ctx->n_sel_creds = 0; + ctx->int_cb_used = 0; + ctx->vparam = NULL; + ctx->cb_vparam = 0; +} + +static void +khuiint_copy_context(khui_action_context * ctxdest, + const khui_action_context * ctxsrc) +{ + ctxdest->scope = ctxsrc->scope; + + if (ctxsrc->scope == KHUI_SCOPE_IDENT) { + ctxdest->identity = ctxsrc->identity; + kcdb_identity_hold(ctxsrc->identity); + } else if (ctxsrc->scope == KHUI_SCOPE_CREDTYPE) { + ctxdest->identity = ctxsrc->identity; + ctxdest->cred_type = ctxsrc->cred_type; + if (ctxsrc->identity != NULL) + kcdb_identity_hold(ctxsrc->identity); + } else if (ctxsrc->scope == KHUI_SCOPE_CRED) { + kcdb_cred_get_identity(ctxsrc->cred, &ctxdest->identity); + kcdb_cred_get_type(ctxsrc->cred, &ctxdest->cred_type); + ctxdest->cred = ctxsrc->cred; + kcdb_cred_hold(ctxsrc->cred); + } else if (ctxsrc->scope == KHUI_SCOPE_GROUP) { + khm_size cb_total; + int i; + + ctxdest->n_headers = ctxsrc->n_headers; + cb_total = 0; + for (i=0; i < (int) ctxsrc->n_headers; i++) { + cb_total += UBOUND32(ctxsrc->headers[i].cb_data); + } + + if (ctxdest->int_cb_buf < cb_total) { + + if (ctxdest->int_buf) + free(ctxdest->int_buf); + + ctxdest->int_cb_buf = cb_total; + ctxdest->int_buf = malloc(cb_total); + } + +#ifdef DEBUG + assert(ctxdest->int_buf || cb_total == 0); +#endif + ctxdest->int_cb_used = 0; + + for (i=0; i < (int) ctxsrc->n_headers; i++) { + ctxdest->headers[i].attr_id = ctxsrc->headers[i].attr_id; + ctxdest->headers[i].cb_data = ctxsrc->headers[i].cb_data; + if (ctxsrc->headers[i].cb_data > 0) { + ctxdest->headers[i].data = + BYTEOFFSET(ctxdest->int_buf, + ctxdest->int_cb_used); + memcpy(ctxdest->headers[i].data, + ctxsrc->headers[i].data, + ctxsrc->headers[i].cb_data); + ctxdest->int_cb_used += + UBOUND32(ctxsrc->headers[i].cb_data); + } else { + ctxdest->headers[i].data = NULL; + } + } + } + + if (ctxsrc->credset) { + + if (ctxdest->credset == NULL) + kcdb_credset_create(&ctxdest->credset); +#ifdef DEBUG + assert(ctxdest->credset != NULL); +#endif + + kcdb_credset_flush(ctxdest->credset); + + kcdb_credset_extract_filtered(ctxdest->credset, + ctxsrc->credset, + khuiint_filter_selected, + NULL); + + kcdb_credset_get_size(ctxdest->credset, + &ctxdest->n_sel_creds); + } else { + if (ctxdest->credset != NULL) + kcdb_credset_flush(ctxdest->credset); + ctxdest->n_sel_creds = 0; + } + + /* For now, we simply transfer the vparam buffer into the new + context. If we are copying, we also need to modify + khui_context_release() to free the allocated buffer */ +#if 0 + if (ctxsrc->vparam && ctxsrc->cb_vparam) { + ctxdest->vparam = malloc(ctxsrc->cb_vparam); +#ifdef DEBUG + assert(ctxdest->vparam); +#endif + memcpy(ctxdest->vparam, ctxsrc->vparam, ctxsrc->cb_vparam); + ctxdest->cb_vparam = ctxsrc->cb_vparam; + } else { +#endif + ctxdest->vparam = ctxsrc->vparam; + ctxdest->cb_vparam = ctxsrc->cb_vparam; +#if 0 + } +#endif +} + +static void +khuiint_context_init(khui_action_context * ctx) { + ctx->magic = KHUI_ACTION_CONTEXT_MAGIC; + ctx->scope = KHUI_SCOPE_NONE; + ctx->identity = NULL; + ctx->cred_type = KCDB_CREDTYPE_INVALID; + ctx->cred = NULL; + ZeroMemory(ctx->headers, sizeof(ctx->headers)); + ctx->n_headers = 0; + ctx->credset = NULL; + ctx->n_sel_creds = 0; + ctx->int_buf = NULL; + ctx->int_cb_buf = 0; + ctx->int_cb_used = 0; + ctx->vparam = NULL; + ctx->cb_vparam = 0; +} + +khui_action_context khui_ctx = { + KHUI_ACTION_CONTEXT_MAGIC, + KHUI_SCOPE_NONE, + NULL, + KCDB_CREDTYPE_INVALID, + NULL, + { + {KCDB_ATTR_INVALID,NULL,0}, + {KCDB_ATTR_INVALID,NULL,0}, + {KCDB_ATTR_INVALID,NULL,0}, + {KCDB_ATTR_INVALID,NULL,0}, + {KCDB_ATTR_INVALID,NULL,0}, + {KCDB_ATTR_INVALID,NULL,0} + }, + 0, + NULL, + 0, + NULL, + 0, + 0, + NULL, + 0}; + +KHMEXP void KHMAPI +khui_context_create(khui_action_context * ctx, + khui_scope scope, + khm_handle identity, + khm_int32 cred_type, + khm_handle cred) +{ + khui_action_context tctx; + + khuiint_context_init(&tctx); + khuiint_context_init(ctx); + + tctx.scope = scope; + tctx.identity = identity; + tctx.cred_type = cred_type; + tctx.cred = cred; + + khuiint_copy_context(ctx, &tctx); +} + +KHMEXP void KHMAPI +khui_context_set(khui_scope scope, + khm_handle identity, + khm_int32 cred_type, + khm_handle cred, + khui_header *headers, + khm_size n_headers, + khm_handle cs_src) { + + khui_context_set_ex(scope, + identity, + cred_type, + cred, + headers, + n_headers, + cs_src, + NULL, + 0); +} + +KHMEXP void KHMAPI +khui_context_set_ex(khui_scope scope, + khm_handle identity, + khm_int32 cred_type, + khm_handle cred, + khui_header *headers, + khm_size n_headers, + khm_handle cs_src, + void * vparam, + khm_size cb_vparam) +{ + khui_action_context tctx; + + EnterCriticalSection(&cs_actions); + + khuiint_context_release(&khui_ctx); + + khuiint_context_init(&tctx); + + tctx.scope = scope; + tctx.identity = identity; + tctx.cred_type = cred_type; + tctx.cred = cred; + if (headers) { + tctx.n_headers = n_headers; + memcpy(tctx.headers, + headers, + sizeof(*headers) * n_headers); + } else { + tctx.n_headers = 0; + } + tctx.credset = cs_src; + tctx.n_sel_creds = 0; /* ignored */ + tctx.vparam = vparam; + tctx.cb_vparam = cb_vparam; + tctx.int_buf = NULL; + tctx.int_cb_buf = 0; + tctx.int_cb_used = 0; + + khuiint_copy_context(&khui_ctx, &tctx); + + khui_context_refresh(); + + LeaveCriticalSection(&cs_actions); +} + +KHMEXP void KHMAPI +khui_context_refresh(void) { + khm_int32 flags; + + EnterCriticalSection(&cs_actions); + if (khui_ctx.identity) { + /* an identity is selected */ + + if (KHM_SUCCEEDED(kcdb_identity_get_flags(khui_ctx.identity, + &flags)) && + (flags & KCDB_IDENT_FLAG_DEFAULT)) { + khui_check_action(KHUI_ACTION_SET_DEF_ID, TRUE); + khui_enable_action(KHUI_ACTION_SET_DEF_ID, FALSE); + } else { + khui_check_action(KHUI_ACTION_SET_DEF_ID, FALSE); + khui_enable_action(KHUI_ACTION_SET_DEF_ID, TRUE); + } + + khui_enable_action(KHUI_ACTION_PASSWD_ID, TRUE); + } else { + khui_check_action(KHUI_ACTION_SET_DEF_ID, FALSE); + khui_enable_action(KHUI_ACTION_SET_DEF_ID, FALSE); + khui_enable_action(KHUI_ACTION_PASSWD_ID, FALSE); + } + + if (khui_ctx.scope != KHUI_SCOPE_NONE) { + khui_enable_action(KHUI_ACTION_PROPERTIES, TRUE); + khui_enable_action(KHUI_ACTION_DESTROY_CRED, TRUE); + khui_enable_action(KHUI_ACTION_RENEW_CRED, TRUE); + } else { + khui_enable_action(KHUI_ACTION_PROPERTIES, FALSE); + khui_enable_action(KHUI_ACTION_DESTROY_CRED, FALSE); + khui_enable_action(KHUI_ACTION_RENEW_CRED, FALSE); + } + + LeaveCriticalSection(&cs_actions); + + kmq_post_message(KMSG_ACT, KMSG_ACT_REFRESH, 0, 0); +} + +KHMEXP void KHMAPI +khui_context_get(khui_action_context * ctx) +{ + EnterCriticalSection(&cs_actions); + + khuiint_context_init(ctx); + khuiint_copy_context(ctx, &khui_ctx); + + if (ctx->credset) { + kcdb_credset_seal(ctx->credset); + } + + LeaveCriticalSection(&cs_actions); +} + +KHMEXP void KHMAPI +khui_context_release(khui_action_context * ctx) +{ +#ifdef DEBUG + assert(ctx->magic == KHUI_ACTION_CONTEXT_MAGIC); +#endif + + khuiint_context_release(ctx); + if (ctx->credset) { + kcdb_credset_unseal(ctx->credset); + kcdb_credset_delete(ctx->credset); + } + ctx->credset = NULL; + if (ctx->int_buf) + free(ctx->int_buf); + ctx->int_buf = NULL; +#if 0 + if (ctx->vparam && ctx->cb_vparam > 0) { + free(ctx->vparam); + ctx->vparam = NULL; + } + ctx->cb_vparam = 0; +#else + ctx->vparam = 0; + ctx->cb_vparam = 0; +#endif +} + +KHMEXP void KHMAPI +khui_context_reset(void) +{ + EnterCriticalSection(&cs_actions); + + khuiint_context_release(&khui_ctx); + + khui_context_refresh(); + + LeaveCriticalSection(&cs_actions); +} + +KHMEXP khm_int32 KHMAPI +khui_context_cursor_filter(khm_handle cred, + khm_int32 flags, + void * rock) { + khui_action_context * ctx = (khui_action_context *) rock; + khm_int32 rv; + + if (ctx->scope == KHUI_SCOPE_NONE) + return 0; + else if (ctx->scope == KHUI_SCOPE_IDENT) { + khm_handle c_ident; + + if (KHM_FAILED(kcdb_cred_get_identity(cred, &c_ident))) + return 0; + + rv = (c_ident == ctx->identity); + + kcdb_identity_release(c_ident); + + return rv; + } else if (ctx->scope == KHUI_SCOPE_CREDTYPE) { + khm_handle c_ident; + khm_int32 c_type; + + if (KHM_FAILED(kcdb_cred_get_type(cred, &c_type)) || + c_type != ctx->cred_type) + return 0; + + if (ctx->identity == NULL) + return 1; + + if (KHM_FAILED(kcdb_cred_get_identity(cred, &c_ident))) + return 0; + + rv = (c_ident == ctx->identity); + + kcdb_identity_release(c_ident); + + return rv; + } else if (ctx->scope == KHUI_SCOPE_CRED) { + return kcdb_creds_is_equal(cred, ctx->cred); + } else if (ctx->scope == KHUI_SCOPE_GROUP) { + int i; + + rv = 1; + + for (i=0; i < (int) ctx->n_headers && rv; i++) { + kcdb_attrib * pattr; + kcdb_type * ptype; + DWORD buffer[1024]; /* 4096 bytes */ + khm_size cb; + + if (kcdb_cred_get_attr(cred, ctx->headers[i].attr_id, + NULL, + NULL, + &cb) != KHM_ERROR_TOO_LONG) { + /* the header doesn't exist anyway */ + rv = (ctx->headers[i].cb_data == 0); + continue; + } +#ifdef DEBUG + assert(cb <= sizeof(buffer)); +#endif + cb = sizeof(buffer); + + if (KHM_FAILED(kcdb_cred_get_attr(cred, + ctx->headers[i].attr_id, + NULL, + (void *) buffer, + &cb))) { + rv = 0; + continue; + } + + if (KHM_FAILED(kcdb_attrib_get_info(ctx->headers[i].attr_id, + &pattr))) { + rv = 0; + continue; + } + + if (KHM_FAILED(kcdb_type_get_info(pattr->type, &ptype))) { + rv = 0; + kcdb_attrib_release_info(pattr); + continue; + } + + if ((*ptype->comp)(ctx->headers[i].data, + ctx->headers[i].cb_data, + (void *) buffer, + cb) != 0) + rv = 1; + + kcdb_type_release_info(ptype); + kcdb_attrib_release_info(pattr); + } + + return rv; + } else + return 0; +} diff --git a/src/windows/identity/uilib/actiondef.cfg b/src/windows/identity/uilib/actiondef.cfg new file mode 100644 index 000000000..14600b0b6 --- /dev/null +++ b/src/windows/identity/uilib/actiondef.cfg @@ -0,0 +1,64 @@ +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, copy, +# modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + +$file_prefix = <<EOS; +/* +This file was autogenerated from src/ui/actiondef.cfg and src/ui/actions.csv. + +Do not modify directly. +*/ + +#include<khuidefs.h> +#include<khhelp.h> +#include"../ui/resource.h" + +khui_action khui_actions [] = { +EOS + +$record_prefix = "{"; + +$record_sep = ",\n"; + +$record_postfix = "}"; + +$file_postfix = <<EOS; + +}; + +int khui_n_actions = sizeof(khui_actions) / sizeof(khui_action); + +EOS + +$skip_lines = 1; + +sub rec_handler { + $arr = shift; + if($$arr[2] =~ /^$/) { + $$arr[2] = "NULL"; + } else { + $$arr[2] = "L\"".$$arr[2]."\""; + } +} + +$record_parser = \&rec_handler; diff --git a/src/windows/identity/uilib/actions.csv b/src/windows/identity/uilib/actions.csv new file mode 100644 index 000000000..e317c7cc5 --- /dev/null +++ b/src/windows/identity/uilib/actions.csv @@ -0,0 +1,37 @@ +Command,Type,Name,Img Normal,Img Hot,Img Disabled,Ico Normal,Ico Disabled,Caption,Tooltip,Topic,State +KHUI_MENU_FILE,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_MENU_FILE,0,IDH_MENU_FILE,0 +KHUI_MENU_CRED,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_MENU_CRED,0,IDH_MENU_CRED,0 +KHUI_MENU_VIEW,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_MENU_VIEW,0,IDH_MENU_VIEW,0 +KHUI_MENU_OPTIONS,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_MENU_OPTIONS,0,IDH_MENU_OPTIONS,0 +KHUI_MENU_HELP,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_MENU_HELP,0,IDH_MENU_HELP,0 +KHUI_MENU_LAYOUT,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_MENU_LAYOUT,0,0,0 +KHUI_MENU_TOOLBARS,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_MENU_TOOLBARS,0,0,0 +KHUI_ACTION_PROPERTIES,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_ACTION_PROPERTIES,0,IDH_ACTION_PROPERTIES,0 +KHUI_ACTION_EXIT,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_ACTION_EXIT,0,IDH_ACTION_EXIT,0 +KHUI_ACTION_SET_DEF_ID,KHUI_ACTIONTYPE_TRIGGER | KHUI_ACTIONTYPE_TOGGLE,,0,0,0,0,0,IDS_ACTION_SET_DEF_ID,0,IDH_ACTION_SET_DEF_ID,0 +KHUI_ACTION_SET_SRCH_ID,KHUI_ACTIONTYPE_TRIGGER | KHUI_ACTIONTYPE_TOGGLE,,0,0,0,0,0,IDS_ACTION_SET_SRCH_ID,0,IDH_ACTION_SET_SRCH_ID,KHUI_ACTIONSTATE_DISABLED +KHUI_ACTION_PASSWD_ID,KHUI_ACTIONTYPE_TRIGGER,,IDB_CHPW,0,IDB_CHPW_DIS,IDB_CHPW_SM,IDB_CHPW_DIS_SM,IDS_ACTION_PASSWD_ID,0,IDH_ACTION_PASSWD_ID,0 +KHUI_ACTION_NEW_CRED,KHUI_ACTIONTYPE_TRIGGER,,IDB_TK_NEW,0,IDB_TK_NEW_DIS,IDB_TK_NEW_SM,IDB_TK_NEW_DIS_SM,IDS_ACTION_NEW_CRED,0,IDH_ACTION_NEW_CRED,0 +KHUI_ACTION_RENEW_CRED,KHUI_ACTIONTYPE_TRIGGER,,IDB_TK_REFRESH,0,IDB_TK_REFRESH_DIS,IDB_TK_REFRESH_SM,IDB_TK_REFRESH_DIS_SM,IDS_ACTION_RENEW_CRED,0,0,0 +KHUI_ACTION_DESTROY_CRED,KHUI_ACTIONTYPE_TRIGGER,,IDB_TK_DELETE,0,IDB_TK_DELETE_DIS,IDB_TK_DELETE_SM,IDB_TK_DELETE_DIS_SM,IDS_ACTION_DESTROY_CRED,0,0,0 +KHUI_ACTION_LAYOUT_ID,KHUI_ACTIONTYPE_TRIGGER | KHUI_ACTIONTYPE_TOGGLE,,0,0,0,0,0,IDS_ACTION_LAYOUT_ID,0,0,KHUI_ACTIONSTATE_CHECKED +KHUI_ACTION_LAYOUT_TYPE,KHUI_ACTIONTYPE_TRIGGER | KHUI_ACTIONTYPE_TOGGLE,,0,0,0,0,0,IDS_ACTION_LAYOUT_TYPE,0,0,KHUI_ACTIONSTATE_DISABLED +KHUI_ACTION_LAYOUT_LOC,KHUI_ACTIONTYPE_TRIGGER | KHUI_ACTIONTYPE_TOGGLE,,0,0,0,0,0,IDS_ACTION_LAYOUT_LOC,0,0,0 +KHUI_ACTION_TB_STANDARD,KHUI_ACTIONTYPE_TRIGGER | KHUI_ACTIONTYPE_TOGGLE,,0,0,0,0,0,IDS_ACTION_TB_STANDARD,0,0,KHUI_ACTIONSTATE_CHECKED +KHUI_ACTION_CHOOSE_COLS,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_ACTION_CHOOSE_COLS,0,IDH_ACTION_CHOOSE_COLS,KHUI_ACTIONSTATE_DISABLED +KHUI_ACTION_DEBUG_WINDOW,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_ACTION_DEBUG_WINDOW,0,IDH_ACTION_DEBUG_WINDOW,KHUI_ACTIONSTATE_DISABLED +KHUI_ACTION_VIEW_REFRESH,KHUI_ACTIONTYPE_TRIGGER,,IDB_VW_REFRESH,0,0,IDB_VW_REFRESH_SM,0,IDS_ACTION_VIEW_REFRESH,0,IDH_ACTION_VIEW_REFRESH,0 +KHUI_ACTION_OPT_IDENTS,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_ACTION_OPT_IDENTS,0,IDH_ACTION_OPT_INIT,0 +KHUI_ACTION_OPT_KHIM,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_ACTION_OPT_KHIM,0,IDH_ACTION_OPT_KHIM,0 +KHUI_ACTION_OPT_NOTIF,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_ACTION_OPT_NOTIF,0,IDH_ACTION_OPT_NOTIF,0 +KHUI_ACTION_HELP_CTX,KHUI_ACTIONTYPE_TRIGGER,,IDB_HELP,0,0,IDB_HELP_SM,0,IDS_ACTION_HELP_CTX,0,0,0 +KHUI_ACTION_HELP_CONTENTS,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_ACTION_HELP_CONTENTS,0,0,0 +KHUI_ACTION_HELP_INDEX,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_ACTION_HELP_INDEX,0,0,0 +KHUI_ACTION_HELP_ABOUT,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_ACTION_HELP_ABOUT,0,0,0 +KHUI_ACTION_OPEN_APP,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_ACTION_OPEN_APP,0,0,0 +KHUI_ACTION_CLOSE_APP,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_ACTION_CLOSE_APP,0,0,0 +KHUI_ACTION_IMPORT,KHUI_ACTIONTYPE_TRIGGER,,IDB_IMPORT,0,IDB_IMPORT_DIS,IDB_IMPORT_SM,IDB_IMPORT_SM_DIS,IDS_ACTION_IMPORT,0,0,0 +KHUI_PACTION_OK,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_PACTION_OK,0,0,0 +KHUI_PACTION_CANCEL,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_PACTION_CANCEL,0,0,0 +KHUI_PACTION_CLOSE,KHUI_ACTIONTYPE_TRIGGER,,0,0,0,0,0,IDS_PACTION_CLOSE,0,0,0 +KHUI_PACTION_BLANK,0,,IDB_TB_SPACE,0,IDB_TB_SPACE,IDB_TB_BLANK_SM,IDB_TB_BLANK_SM,0,0,0,KHUI_ACTIONSTATE_DISABLED diff --git a/src/windows/identity/uilib/alert.c b/src/windows/identity/uilib/alert.c new file mode 100644 index 000000000..69ef01f93 --- /dev/null +++ b/src/windows/identity/uilib/alert.c @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khuidefs.h> +#include<assert.h> + +/*********************************************************************** + Alerter +***********************************************************************/ + +khui_alert * kh_alerts = NULL; +CRITICAL_SECTION cs_alerts; + +void +alert_init(void) +{ + InitializeCriticalSection(&cs_alerts); +} + +void +alert_exit(void) +{ + DeleteCriticalSection(&cs_alerts); +} + +KHMEXP khm_int32 KHMAPI +khui_alert_create_empty(khui_alert ** result) +{ + khui_alert * a; + + a = malloc(sizeof(*a)); + ZeroMemory(a, sizeof(*a)); + + a->magic = KHUI_ALERT_MAGIC; + + /* set defaults */ + a->severity = KHERR_INFO; + a->flags = KHUI_ALERT_FLAG_FREE_STRUCT; + + khui_alert_hold(a); + EnterCriticalSection(&cs_alerts); + LPUSH(&kh_alerts, a); + LeaveCriticalSection(&cs_alerts); + + *result = a; + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_alert_create_simple(const wchar_t * title, + const wchar_t * message, + khm_int32 severity, + khui_alert ** result) +{ + khui_alert * a; + + khui_alert_create_empty(&a); + khui_alert_set_title(a, title); + khui_alert_set_message(a, message); + khui_alert_set_severity(a, severity); + + *result = a; + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_alert_set_title(khui_alert * alert, const wchar_t * title) +{ + size_t cb = 0; + + assert(alert->magic == KHUI_ALERT_MAGIC); + + if(title) { + if(FAILED(StringCbLength(title, + KHUI_MAXCB_TITLE - sizeof(wchar_t), + &cb))) { + return KHM_ERROR_INVALID_PARM; + } + cb += sizeof(wchar_t); + } + + EnterCriticalSection(&cs_alerts); + if(alert->title && (alert->flags & KHUI_ALERT_FLAG_FREE_TITLE)) { + free(alert->title); + alert->title = NULL; + alert->flags &= ~KHUI_ALERT_FLAG_FREE_TITLE; + } + if(title) { + alert->title = malloc(cb); + StringCbCopy(alert->title, cb, title); + alert->flags |= KHUI_ALERT_FLAG_FREE_TITLE; + } + LeaveCriticalSection(&cs_alerts); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_alert_set_flags(khui_alert * alert, khm_int32 mask, khm_int32 flags) +{ + assert(alert->magic == KHUI_ALERT_MAGIC); + + if (mask & ~KHUI_ALERT_FLAGMASK_RDWR) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_alerts); + alert->flags = + (alert->flags & ~mask) | + (flags & mask); + LeaveCriticalSection(&cs_alerts); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_alert_set_severity(khui_alert * alert, khm_int32 severity) +{ + + assert(alert->magic == KHUI_ALERT_MAGIC); + + EnterCriticalSection(&cs_alerts); + alert->severity = severity; + LeaveCriticalSection(&cs_alerts); + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_alert_set_suggestion(khui_alert * alert, + const wchar_t * suggestion) { + size_t cb = 0; + + assert(alert->magic == KHUI_ALERT_MAGIC); + + if(suggestion) { + if(FAILED(StringCbLength(suggestion, + KHUI_MAXCB_MESSAGE - sizeof(wchar_t), + &cb))) { + return KHM_ERROR_INVALID_PARM; + } + cb += sizeof(wchar_t); + } + + EnterCriticalSection(&cs_alerts); + if(alert->suggestion && + (alert->flags & KHUI_ALERT_FLAG_FREE_SUGGEST)) { + + free(alert->suggestion); + alert->suggestion = NULL; + alert->flags &= ~KHUI_ALERT_FLAG_FREE_SUGGEST; + + } + + if(suggestion) { + alert->suggestion = malloc(cb); + StringCbCopy(alert->suggestion, cb, suggestion); + alert->flags |= KHUI_ALERT_FLAG_FREE_SUGGEST; + } + LeaveCriticalSection(&cs_alerts); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_alert_set_message(khui_alert * alert, const wchar_t * message) +{ + size_t cb = 0; + + assert(alert->magic == KHUI_ALERT_MAGIC); + + if(message) { + if(FAILED(StringCbLength(message, + KHUI_MAXCB_MESSAGE - sizeof(wchar_t), + &cb))) { + return KHM_ERROR_INVALID_PARM; + } + cb += sizeof(wchar_t); + } + + EnterCriticalSection(&cs_alerts); + if(alert->message && + (alert->flags & KHUI_ALERT_FLAG_FREE_MESSAGE)) { + + free(alert->message); + alert->message = NULL; + alert->flags &= ~KHUI_ALERT_FLAG_FREE_MESSAGE; + + } + + if(message) { + alert->message = malloc(cb); + StringCbCopy(alert->message, cb, message); + alert->flags |= KHUI_ALERT_FLAG_FREE_MESSAGE; + } + LeaveCriticalSection(&cs_alerts); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_alert_clear_commands(khui_alert * alert) +{ + assert(alert->magic == KHUI_ALERT_MAGIC); + + EnterCriticalSection(&cs_alerts); + alert->n_alert_commands = 0; + LeaveCriticalSection(&cs_alerts); + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_alert_add_command(khui_alert * alert, khm_int32 command_id) +{ + khm_int32 rv = KHM_ERROR_SUCCESS; + + assert(alert->magic == KHUI_ALERT_MAGIC); + + EnterCriticalSection(&cs_alerts); + if(alert->n_alert_commands >= KHUI_MAX_ALERT_COMMANDS) + rv = KHM_ERROR_NO_RESOURCES; + else { + alert->alert_commands[alert->n_alert_commands++] = command_id; + } + LeaveCriticalSection(&cs_alerts); + + return rv; +} + +KHMEXP khm_int32 KHMAPI +khui_alert_show(khui_alert * alert) +{ + assert(alert->magic == KHUI_ALERT_MAGIC); + + khui_alert_hold(alert); + /* the alert will be released when the message is processed */ + kmq_post_message(KMSG_ALERT, KMSG_ALERT_SHOW, 0, (void *) alert); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_alert_show_simple(const wchar_t * title, + const wchar_t * message, + khm_int32 severity) +{ + khui_alert * a = NULL; + khm_int32 rv; + + rv = khui_alert_create_simple(title, message, severity, &a); + + if(KHM_FAILED(rv)) + return rv; + + rv = khui_alert_show(a); + + khui_alert_release(a); + + return rv; +} + +KHMEXP khm_int32 KHMAPI +khui_alert_hold(khui_alert * alert) +{ + assert(alert->magic == KHUI_ALERT_MAGIC); + + EnterCriticalSection(&cs_alerts); + alert->refcount++; + LeaveCriticalSection(&cs_alerts); + return KHM_ERROR_SUCCESS; +} + +/* called with cs_alert held */ +static void +free_alert(khui_alert * alert) +{ + assert(alert->magic == KHUI_ALERT_MAGIC); + + LDELETE(&kh_alerts, alert); + + if(alert->flags & KHUI_ALERT_FLAG_FREE_TITLE) { + assert(alert->title); + free(alert->title); + alert->title = NULL; + alert->flags &= ~KHUI_ALERT_FLAG_FREE_TITLE; + } + if(alert->flags & KHUI_ALERT_FLAG_FREE_MESSAGE) { + assert(alert->message); + free(alert->message); + alert->message = NULL; + alert->flags &= ~KHUI_ALERT_FLAG_FREE_MESSAGE; + } + if(alert->flags & KHUI_ALERT_FLAG_FREE_SUGGEST) { + assert(alert->suggestion); + free(alert->suggestion); + alert->suggestion = NULL; + alert->flags &= ~KHUI_ALERT_FLAG_FREE_SUGGEST; + } + if(alert->flags & KHUI_ALERT_FLAG_FREE_STRUCT) { + alert->flags &= ~KHUI_ALERT_FLAG_FREE_STRUCT; + alert->magic = 0; + free(alert); + } +} + +KHMEXP khm_int32 KHMAPI +khui_alert_release(khui_alert * alert) +{ + assert(alert->magic == KHUI_ALERT_MAGIC); + + EnterCriticalSection(&cs_alerts); + if((--(alert->refcount)) == 0) { + free_alert(alert); + } + LeaveCriticalSection(&cs_alerts); + return KHM_ERROR_SUCCESS; +} + +KHMEXP void KHMAPI khui_alert_lock(khui_alert * alert) +{ + EnterCriticalSection(&cs_alerts); +} + +KHMEXP void KHMAPI khui_alert_unlock(khui_alert * alert) +{ + LeaveCriticalSection(&cs_alerts); +} diff --git a/src/windows/identity/uilib/configui.c b/src/windows/identity/uilib/configui.c new file mode 100644 index 000000000..5c97c701f --- /dev/null +++ b/src/windows/identity/uilib/configui.c @@ -0,0 +1,1001 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khuidefs.h> +#include<kmm.h> +#include<configui.h> +#include<assert.h> + +khm_int32 cfgui_node_serial; +LONG init_once = 0; +CRITICAL_SECTION cs_cfgui; +khui_config_node_i * cfgui_root_config; +HWND hwnd_cfgui = NULL; + +static khui_config_node_i * +cfgui_create_new_node(void) { + khui_config_node_i * node; + + node = malloc(sizeof(*node)); +#ifdef DEBUG + assert(node); +#endif + ZeroMemory(node, sizeof(*node)); + node->magic = KHUI_CONFIG_NODE_MAGIC; + + EnterCriticalSection(&cs_cfgui); + node->id = ++cfgui_node_serial; + LeaveCriticalSection(&cs_cfgui); + + return node; +} + +/* called with cs_cfgui held */ +static void +cfgui_free_node(khui_config_node_i * node) { + if (!cfgui_is_valid_node(node)) + return; + + if (node->reg.name) + free((void *) node->reg.name); + + if (node->reg.short_desc) + free((void *) node->reg.short_desc); + + if (node->reg.long_desc) + free((void *) node->reg.long_desc); + + node->magic = 0; + + if (node->owner) + kmm_release_plugin(node->owner); + + ZeroMemory(node, sizeof(*node)); + + free(node); +} + + +static void +cfgui_hold_node(khui_config_node_i * node) { + EnterCriticalSection(&cs_cfgui); + node->refcount++; + LeaveCriticalSection(&cs_cfgui); +} + + +static void +cfgui_release_node(khui_config_node_i * node) { + EnterCriticalSection(&cs_cfgui); + node->refcount--; + if (node->refcount == 0 && + (node->flags & KHUI_CN_FLAG_DELETED)) { + khui_config_node_i * parent; + parent = TPARENT(node); +#ifdef DEBUG + assert(TFIRSTCHILD(node) == NULL); + assert(parent != NULL); +#endif + TDELCHILD(parent, node); + cfgui_free_node(node); + cfgui_release_node(parent); + } + LeaveCriticalSection(&cs_cfgui); +} + +static void +cfgui_init_once(void) { + if (init_once == 0 && + InterlockedIncrement(&init_once) == 1) { + InitializeCriticalSection(&cs_cfgui); + cfgui_root_config = cfgui_create_new_node(); + cfgui_node_serial = 0; + hwnd_cfgui = NULL; + } +} + +KHMEXP khm_int32 KHMAPI +khui_cfg_register(khui_config_node vparent, + const khui_config_node_reg * reg) { + + size_t cb_name; + size_t cb_short_desc; + size_t cb_long_desc; + khui_config_node_i * node; + khui_config_node_i * parent; + khui_config_node t; + wchar_t * name; + wchar_t * short_desc; + wchar_t * long_desc; + + cfgui_init_once(); + + if (!reg || + FAILED(StringCbLength(reg->name, + KHUI_MAXCB_NAME, + &cb_name)) || + FAILED(StringCbLength(reg->short_desc, + KHUI_MAXCB_SHORT_DESC, + &cb_short_desc)) || + FAILED(StringCbLength(reg->long_desc, + KHUI_MAXCB_LONG_DESC, + &cb_long_desc)) || + (vparent && + !cfgui_is_valid_node_handle(vparent))) + return KHM_ERROR_INVALID_PARM; + + if (KHM_SUCCEEDED(khui_cfg_open(vparent, + reg->name, + &t))) { + khui_cfg_release(t); + return KHM_ERROR_DUPLICATE; + } + + cb_name += sizeof(wchar_t); + cb_short_desc += sizeof(wchar_t); + cb_long_desc += sizeof(wchar_t); + + node = cfgui_create_new_node(); + + node->reg = *reg; + node->reg.flags &= KHUI_CNFLAGMASK_STATIC; + + name = malloc(cb_name); + StringCbCopy(name, cb_name, reg->name); + short_desc = malloc(cb_short_desc); + StringCbCopy(short_desc, cb_short_desc, reg->short_desc); + long_desc = malloc(cb_long_desc); + StringCbCopy(long_desc, cb_long_desc, reg->long_desc); + + node->reg.name = name; + node->reg.short_desc = short_desc; + node->reg.long_desc = long_desc; + node->flags = node->reg.flags; + + if (vparent == NULL) { + parent = cfgui_root_config; + } else { + parent = cfgui_node_i_from_handle(vparent); + } + + //node->owner = kmm_this_plugin(); + + EnterCriticalSection(&cs_cfgui); + TADDCHILD(parent, node); + LeaveCriticalSection(&cs_cfgui); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_cfg_open(khui_config_node vparent, + const wchar_t * name, + khui_config_node * result) { + khui_config_node_i * parent; + khui_config_node_i * c; + size_t sz; + + cfgui_init_once(); + + if ((vparent && + !cfgui_is_valid_node_handle(vparent)) || + FAILED(StringCbLength(name, KHUI_MAXCCH_NAME, &sz)) || + !result) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_cfgui); + if (vparent) + parent = cfgui_node_i_from_handle(vparent); + else + parent = cfgui_root_config; + + c = TFIRSTCHILD(parent); + while(c) { + if (!(c->flags & KHUI_CN_FLAG_DELETED) && + !wcscmp(c->reg.name, name)) + break; + c = LNEXT(c); + } + + if (c) { + *result = cfgui_handle_from_node_i(c); + cfgui_hold_node(c); + } else { + *result = NULL; + } + LeaveCriticalSection(&cs_cfgui); + + if (*result) + return KHM_ERROR_SUCCESS; + else + return KHM_ERROR_NOT_FOUND; +} + +KHMEXP khm_int32 KHMAPI +khui_cfg_remove(khui_config_node vnode) { + khui_config_node_i * node; + if (!cfgui_is_valid_node_handle(vnode)) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_cfgui); + node = cfgui_node_i_from_handle(vnode); + node->flags |= KHUI_CN_FLAG_DELETED; + LeaveCriticalSection(&cs_cfgui); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_cfg_hold(khui_config_node vnode) { + if (!cfgui_is_valid_node_handle(vnode)) + return KHM_ERROR_INVALID_PARM; + + cfgui_hold_node(cfgui_node_i_from_handle(vnode)); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_cfg_release(khui_config_node vnode) { + if (!cfgui_is_valid_node_handle(vnode)) + return KHM_ERROR_INVALID_PARM; + + cfgui_release_node(cfgui_node_i_from_handle(vnode)); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_cfg_get_first_child(khui_config_node vparent, + khui_config_node * result) { + khui_config_node_i * parent; + khui_config_node_i * c; + + cfgui_init_once(); + + if((vparent && !cfgui_is_valid_node_handle(vparent)) || + !result) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_cfgui); + if (cfgui_is_valid_node_handle(vparent)) { + parent = cfgui_node_i_from_handle(vparent); + } else if (!vparent) { + parent = cfgui_root_config; + } else { + parent = NULL; + } + + if (parent) { + for(c = TFIRSTCHILD(parent); + c && (c->reg.flags & KHUI_CNFLAG_SUBPANEL); + c = LNEXT(c)); + } else { + c = NULL; + } + + if (c) + cfgui_hold_node(c); + LeaveCriticalSection(&cs_cfgui); + + *result = c; + + if (c) + return KHM_ERROR_SUCCESS; + else + return KHM_ERROR_NOT_FOUND; +} + +KHMEXP khm_int32 KHMAPI +khui_cfg_get_first_subpanel(khui_config_node vparent, + khui_config_node * result) { + khui_config_node_i * parent; + khui_config_node_i * c; + + cfgui_init_once(); + + if((vparent && !cfgui_is_valid_node_handle(vparent)) || + !result) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_cfgui); + if (cfgui_is_valid_node_handle(vparent)) { + parent = cfgui_node_i_from_handle(vparent); + } else if (!vparent) { + parent = cfgui_root_config; + } else { + parent = NULL; + } + + if (parent) { + for(c = TFIRSTCHILD(parent); + c && !(c->reg.flags & KHUI_CNFLAG_SUBPANEL); + c = LNEXT(c)); + } else { + c = NULL; + } + + if (c) + cfgui_hold_node(c); + LeaveCriticalSection(&cs_cfgui); + + *result = c; + + if (c) + return KHM_ERROR_SUCCESS; + else + return KHM_ERROR_NOT_FOUND; +} + + +KHMEXP khm_int32 KHMAPI +khui_cfg_get_next(khui_config_node vnode, + khui_config_node * result) { + + khui_config_node_i * node; + khui_config_node_i * nxt_node; + + if (!cfgui_is_valid_node_handle(vnode) || + !result) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_cfgui); + if (cfgui_is_valid_node_handle(vnode)) { + node = cfgui_node_i_from_handle(vnode); + for(nxt_node = LNEXT(node); + nxt_node && + ((node->reg.flags ^ nxt_node->reg.flags) & + KHUI_CNFLAG_SUBPANEL); + nxt_node = LNEXT(nxt_node)); + if (nxt_node) + cfgui_hold_node(nxt_node); + } else { + nxt_node = NULL; + } + LeaveCriticalSection(&cs_cfgui); + + *result = cfgui_handle_from_node_i(nxt_node); + + if (nxt_node) + return KHM_ERROR_SUCCESS; + else + return KHM_ERROR_NOT_FOUND; +} + +KHMEXP khm_int32 KHMAPI +khui_cfg_get_next_release(khui_config_node * pvnode) { + + khui_config_node_i * node; + khui_config_node_i * nxt_node; + + if (!pvnode || + !cfgui_is_valid_node_handle(*pvnode)) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_cfgui); + if (cfgui_is_valid_node_handle(*pvnode)) { + node = cfgui_node_i_from_handle(*pvnode); + for(nxt_node = LNEXT(node); + nxt_node && + ((node->reg.flags ^ nxt_node->reg.flags) & + KHUI_CNFLAG_SUBPANEL); + nxt_node = LNEXT(nxt_node)); + if (nxt_node) + cfgui_hold_node(nxt_node); + cfgui_release_node(node); + } else { + nxt_node = NULL; + } + LeaveCriticalSection(&cs_cfgui); + + *pvnode = cfgui_handle_from_node_i(nxt_node); + + if (nxt_node) + return KHM_ERROR_SUCCESS; + else + return KHM_ERROR_NOT_FOUND; +} + +KHMEXP khm_int32 KHMAPI +khui_cfg_get_reg(khui_config_node vnode, + khui_config_node_reg * reg) { + + khui_config_node_i * node; + + cfgui_init_once(); + + if ((vnode && !cfgui_is_valid_node_handle(vnode)) || + !reg) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_cfgui); + if (cfgui_is_valid_node_handle(vnode)) { + node = cfgui_node_i_from_handle(vnode); + *reg = node->reg; + } else if (!vnode) { + node = cfgui_root_config; + *reg = node->reg; + } else { + node = NULL; + ZeroMemory(reg, sizeof(*reg)); + } + LeaveCriticalSection(&cs_cfgui); + + if (node) + return KHM_ERROR_SUCCESS; + else + return KHM_ERROR_INVALID_PARM; +} + +KHMEXP HWND KHMAPI +khui_cfg_get_hwnd(khui_config_node vnode) { + khui_config_node_i * node; + HWND hwnd; + + cfgui_init_once(); + + if (vnode && + !cfgui_is_valid_node_handle(vnode)) + return NULL; + + EnterCriticalSection(&cs_cfgui); + if (cfgui_is_valid_node_handle(vnode)) + node = cfgui_node_i_from_handle(vnode); + else if (!vnode) + node = cfgui_root_config; + else + node = NULL; + + if (node) + hwnd = node->hwnd; + else + hwnd = NULL; + LeaveCriticalSection(&cs_cfgui); + + return hwnd; +} + +KHMEXP LPARAM KHMAPI +khui_cfg_get_param(khui_config_node vnode) { + khui_config_node_i * node; + LPARAM param; + + cfgui_init_once(); + + if (vnode && + !cfgui_is_valid_node_handle(vnode)) + return 0; + + EnterCriticalSection(&cs_cfgui); + if (cfgui_is_valid_node_handle(vnode)) + node = cfgui_node_i_from_handle(vnode); + else if (!vnode) + node = cfgui_root_config; + else + node = NULL; + + if (node) + param = node->param; + else + param = 0; + LeaveCriticalSection(&cs_cfgui); + + return param; +} + +KHMEXP void KHMAPI +khui_cfg_set_hwnd(khui_config_node vnode, HWND hwnd) { + khui_config_node_i * node; + + cfgui_init_once(); + + if (vnode && + !cfgui_is_valid_node_handle(vnode)) + return; + + EnterCriticalSection(&cs_cfgui); + if (cfgui_is_valid_node_handle(vnode)) + node = cfgui_node_i_from_handle(vnode); + else if (!vnode) + node = cfgui_root_config; + else + node = NULL; + + if (node) + node->hwnd = hwnd; + LeaveCriticalSection(&cs_cfgui); +} + +KHMEXP void KHMAPI +khui_cfg_set_param(khui_config_node vnode, LPARAM param) { + khui_config_node_i * node; + + cfgui_init_once(); + + if (vnode && + !cfgui_is_valid_node_handle(vnode)) + return; + + EnterCriticalSection(&cs_cfgui); + if (cfgui_is_valid_node_handle(vnode)) + node = cfgui_node_i_from_handle(vnode); + else if (!vnode) + node = cfgui_root_config; + else + node = NULL; + + if (node) + node->param = param; + LeaveCriticalSection(&cs_cfgui); +} + +static cfg_node_data * +get_node_data(khui_config_node_i * node, + void * key, + khm_boolean create) { + khm_size i; + + for (i=0; i<node->n_data; i++) { + if (node->data[i].key == key) + return &(node->data[i]); + } + + if (!create) + return NULL; + + if (node->n_data + 1 > node->nc_data) { + cfg_node_data * newdata; + + node->nc_data = UBOUNDSS((node->n_data + 1), + KHUI_NODEDATA_ALLOC_INCR, + KHUI_NODEDATA_ALLOC_INCR); +#ifdef DEBUG + assert(node->nc_data >= node->n_data + 1); +#endif + newdata = malloc(sizeof(*newdata) * node->nc_data); +#ifdef DEBUG + assert(newdata); +#endif + ZeroMemory(newdata, sizeof(*newdata) * node->nc_data); + + if (node->data && node->n_data > 0) { + memcpy(newdata, node->data, node->n_data * sizeof(*newdata)); + free(node->data); + } + node->data = newdata; + } + + node->data[node->n_data].key = key; + node->n_data++; + + return &(node->data[node->n_data - 1]); +} + +KHMEXP HWND KHMAPI +khui_cfg_get_hwnd_inst(khui_config_node vnode, + khui_config_node noderef) { + khui_config_node_i * node; + cfg_node_data * data; + HWND hwnd; + + cfgui_init_once(); + + if (vnode && + !cfgui_is_valid_node_handle(vnode)) + return NULL; + + EnterCriticalSection(&cs_cfgui); + if (cfgui_is_valid_node_handle(vnode)) + node = cfgui_node_i_from_handle(vnode); + else if (!vnode) + node = cfgui_root_config; + else + node = NULL; + + if (node) { + data = get_node_data(node, noderef, FALSE); + if (data) + hwnd = data->hwnd; + else + hwnd = NULL; + } else + hwnd = NULL; + LeaveCriticalSection(&cs_cfgui); + + return hwnd; +} + +KHMEXP LPARAM KHMAPI +khui_cfg_get_param_inst(khui_config_node vnode, + khui_config_node noderef) { + khui_config_node_i * node; + cfg_node_data * data; + LPARAM lParam; + + cfgui_init_once(); + + if (vnode && + !cfgui_is_valid_node_handle(vnode)) + return 0; + + EnterCriticalSection(&cs_cfgui); + if (cfgui_is_valid_node_handle(vnode)) + node = cfgui_node_i_from_handle(vnode); + else if (!vnode) + node = cfgui_root_config; + else + node = NULL; + + if (node) { + data = get_node_data(node, noderef, FALSE); + if (data) + lParam = data->param; + else + lParam = 0; + } else + lParam = 0; + LeaveCriticalSection(&cs_cfgui); + + return lParam; +} + +KHMEXP void KHMAPI +khui_cfg_set_hwnd_inst(khui_config_node vnode, + khui_config_node noderef, + HWND hwnd) { + khui_config_node_i * node; + cfg_node_data * data; + + cfgui_init_once(); + + if (vnode && + !cfgui_is_valid_node_handle(vnode)) + return; + + EnterCriticalSection(&cs_cfgui); + if (cfgui_is_valid_node_handle(vnode)) + node = cfgui_node_i_from_handle(vnode); + else if (!vnode) + node = cfgui_root_config; + else + node = NULL; + + if (node) { + data = get_node_data(node, noderef, TRUE); + if (data) + data->hwnd = hwnd; + } + LeaveCriticalSection(&cs_cfgui); +} + +KHMEXP void KHMAPI +khui_cfg_set_param_inst(khui_config_node vnode, + khui_config_node noderef, + LPARAM param) { + khui_config_node_i * node; + cfg_node_data * data; + + cfgui_init_once(); + + if (vnode && + !cfgui_is_valid_node_handle(vnode)) + return; + + EnterCriticalSection(&cs_cfgui); + if (cfgui_is_valid_node_handle(vnode)) + node = cfgui_node_i_from_handle(vnode); + else if (!vnode) + node = cfgui_root_config; + else + node = NULL; + + if (node) { + data = get_node_data(node, noderef, TRUE); + if (data) + data->param = param; + } + LeaveCriticalSection(&cs_cfgui); +} + + +/* called with cs_cfgui held */ +static void +cfgui_clear_params(khui_config_node_i * node) { + khui_config_node_i * c; + + node->hwnd = NULL; + node->param = 0; + node->flags &= KHUI_CNFLAGMASK_STATIC; + c = TFIRSTCHILD(node); + while(c) { + cfgui_clear_params(c); + c = LNEXT(c); + } +} + +KHMEXP void KHMAPI +khui_cfg_clear_params(void) { + + cfgui_init_once(); + + EnterCriticalSection(&cs_cfgui); + cfgui_clear_params(cfgui_root_config); + LeaveCriticalSection(&cs_cfgui); +} + +KHMEXP void KHMAPI +khui_cfg_set_configui_handle(HWND hwnd) { + EnterCriticalSection(&cs_cfgui); + hwnd_cfgui = hwnd; + LeaveCriticalSection(&cs_cfgui); +} + +KHMEXP void KHMAPI +khui_cfg_set_flags(khui_config_node vnode, + khm_int32 flags, + khm_int32 mask) { + khui_config_node_i * node; + khm_int32 newflags; + + if (vnode && + !cfgui_is_valid_node_handle(vnode)) + return; + + mask &= KHUI_CNFLAGMASK_DYNAMIC; + + EnterCriticalSection(&cs_cfgui); + if (cfgui_is_valid_node_handle(vnode) && + hwnd_cfgui != NULL) { + + node = cfgui_node_i_from_handle(vnode); + + newflags = + (flags & mask) | + (node->flags & ~mask); + + if (newflags != node->flags) { + node->flags = newflags; + + if (hwnd_cfgui) + PostMessage(hwnd_cfgui, KHUI_WM_CFG_NOTIFY, + MAKEWPARAM((WORD)newflags, WMCFG_UPDATE_STATE), + (LPARAM) vnode); + } + } + LeaveCriticalSection(&cs_cfgui); +} + +/* called with cs_cfgui held */ +static void +recalc_node_flags(khui_config_node vnode) { + khui_config_node_i * node; + khui_config_node_i * parent; + khm_int32 flags; + khm_size i; + +#ifdef DEBUG + assert(cfgui_is_valid_node_handle(vnode)); +#endif + + node = cfgui_node_i_from_handle(vnode); + + parent = TPARENT(node); +#ifdef DEBUG + assert(parent); +#endif + + flags = 0; + + /* this code is wrong. we need to go through all the subpanels in + the parent and pick the data record corresponding to this node + and then merge the flags from there. */ + /* TODO: fix this */ + for (i=0; i < parent->n_data; i++) { + if (parent->data[i].key == vnode) + flags |= parent->data[i].flags; + } + + flags &= KHUI_CNFLAGMASK_DYNAMIC; + + if ((node->flags & flags) != flags) { + node->flags = flags | + (node->flags & ~KHUI_CNFLAGMASK_DYNAMIC); + + if (hwnd_cfgui) + PostMessage(hwnd_cfgui, KHUI_WM_CFG_NOTIFY, + MAKEWPARAM((WORD) node->flags, WMCFG_UPDATE_STATE), + (LPARAM) vnode); + } +} + +KHMEXP void KHMAPI +khui_cfg_set_flags_inst(khui_config_init_data * d, + khm_int32 flags, + khm_int32 mask) { + khui_config_node_i * node; + cfg_node_data * data; + + cfgui_init_once(); + if (!cfgui_is_valid_node_handle(d->this_node)) + return; + + mask &= KHUI_CNFLAGMASK_DYNAMIC; + + EnterCriticalSection(&cs_cfgui); + if (cfgui_is_valid_node_handle(d->this_node)) + node = cfgui_node_i_from_handle(d->this_node); + else + node = NULL; + + if (node) { + data = get_node_data(node, d->ctx_node, TRUE); + if (data) { + khm_int32 new_flags; + + new_flags = (flags & mask) | + (data->flags & ~mask); + + if (new_flags != data->flags) { + data->flags = new_flags; + + if (d->ctx_node != d->ref_node) + recalc_node_flags(d->ctx_node); + } + } + } + LeaveCriticalSection(&cs_cfgui); +} + +KHMEXP khm_int32 KHMAPI +khui_cfg_get_flags(khui_config_node vnode) { + khui_config_node_i * node; + khm_int32 flags = 0; + + if (vnode && + !cfgui_is_valid_node_handle(vnode)) + return 0; + + EnterCriticalSection(&cs_cfgui); + if (cfgui_is_valid_node_handle(vnode) && + hwnd_cfgui != NULL) { + + node = cfgui_node_i_from_handle(vnode); + + flags = node->flags; + } + LeaveCriticalSection(&cs_cfgui); + + return flags; +} + +KHMEXP khm_int32 KHMAPI +khui_cfg_get_name(khui_config_node vnode, + wchar_t * buf, + khm_size * cb_buf) { + khui_config_node_i * node; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if (!cb_buf || + !cfgui_is_valid_node_handle(vnode)) + return KHM_ERROR_INVALID_PARM; + + EnterCriticalSection(&cs_cfgui); + if (cfgui_is_valid_node_handle(vnode) && + hwnd_cfgui != NULL) { + khm_size cb; + + node = cfgui_node_i_from_handle(vnode); + + StringCbLength(node->reg.name, KHUI_MAXCCH_NAME, &cb); + + if (buf == NULL || cb > *cb_buf) { + *cb_buf = cb; + rv = KHM_ERROR_TOO_LONG; + } else { + StringCbCopy(buf, *cb_buf, node->reg.name); + *cb_buf = cb; + } + } else { + rv = KHM_ERROR_INVALID_PARM; + } + LeaveCriticalSection(&cs_cfgui); + + return rv; +} + +KHMEXP khm_int32 KHMAPI +khui_cfg_init_dialog_data(HWND hwnd_dlg, + const khui_config_init_data * data, + khm_size cb_extra, + khui_config_init_data ** new_data, + void ** extra) { + khm_size cb; + khui_config_init_data * d; + + cb = sizeof(khui_config_init_data) + cb_extra; + d = malloc(cb); +#ifdef DEBUG + assert(d); +#endif + ZeroMemory(d, cb); + + *d = *data; + + if (d->ctx_node) + khui_cfg_hold(d->ctx_node); + if (d->this_node) + khui_cfg_hold(d->this_node); + if (d->ref_node) + khui_cfg_hold(d->ref_node); + +#pragma warning(push) +#pragma warning(disable: 4244) + SetWindowLongPtr(hwnd_dlg, DWLP_USER, (LONG_PTR) d); +#pragma warning(pop) + + if (new_data) + *new_data = d; + if (extra) + *extra = (void *) (d + 1); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_cfg_get_dialog_data(HWND hwnd_dlg, + khui_config_init_data ** data, + void ** extra) { + khui_config_init_data * d; + + d = (khui_config_init_data *) (LONG_PTR) GetWindowLongPtr(hwnd_dlg, + DWLP_USER); +#ifdef DEBUG + assert(d); +#endif + + *data = d; + if (extra) + *extra = (void *) (d + 1); + + return (d)?KHM_ERROR_SUCCESS: KHM_ERROR_NOT_FOUND; +} + +KHMEXP khm_int32 KHMAPI +khui_cfg_free_dialog_data(HWND hwnd_dlg) { + khui_config_init_data * d; + + d = (khui_config_init_data *) (LONG_PTR) GetWindowLongPtr(hwnd_dlg, + DWLP_USER); +#ifdef DEBUG + assert(d); +#endif + + if (d) { + free(d); + } + + return (d)?KHM_ERROR_SUCCESS: KHM_ERROR_NOT_FOUND; +} diff --git a/src/windows/identity/uilib/configui.h b/src/windows/identity/uilib/configui.h new file mode 100644 index 000000000..37d5e9705 --- /dev/null +++ b/src/windows/identity/uilib/configui.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_CONFIGUI_H +#define __KHIMAIRA_CONFIGUI_H + +typedef struct tag_cfg_node_data { + void * key; + HWND hwnd; + LPARAM param; + khm_int32 flags; +} cfg_node_data; + +typedef struct tag_khui_config_node_i { + khm_int32 magic; + + khui_config_node_reg reg; + kmm_plugin owner; + khm_int32 id; + + HWND hwnd; + LPARAM param; + + cfg_node_data * data; + khm_size n_data; + khm_size nc_data; + + khm_int32 refcount; + khm_int32 flags; + TDCL(struct tag_khui_config_node_i); +} khui_config_node_i; + +#define KHUI_CONFIG_NODE_MAGIC 0x38f4cb52 + +#define KHUI_NODEDATA_ALLOC_INCR 8 + +#define KHUI_CN_FLAG_DELETED 0x0008 + +#define cfgui_is_valid_node_handle(v) \ +((v) && ((khui_config_node_i *) (v))->magic == KHUI_CONFIG_NODE_MAGIC) + +#define cfgui_is_valid_node(n) \ +((n)->magic == KHUI_CONFIG_NODE_MAGIC) + +#define cfgui_node_i_from_handle(v) \ +((khui_config_node_i *) v) + +#define cfgui_handle_from_node_i(n) \ +((khui_config_node) n) + +#endif diff --git a/src/windows/identity/uilib/creddlg.c b/src/windows/identity/uilib/creddlg.c new file mode 100644 index 000000000..ccc27b401 --- /dev/null +++ b/src/windows/identity/uilib/creddlg.c @@ -0,0 +1,671 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khuidefs.h> +#include<assert.h> + +#define CW_ALLOC_INCR 8 + +static void cw_free_prompts(khui_new_creds * c); + +static void cw_free_prompt(khui_new_creds_prompt * p); + +static khui_new_creds_prompt * +cw_create_prompt( + khm_size idx, + khm_int32 type, + wchar_t * prompt, + wchar_t * def, + khm_int32 flags); + +KHMEXP khm_int32 KHMAPI +khui_cw_create_cred_blob(khui_new_creds ** ppnc) +{ + khui_new_creds * c; + + c = malloc(sizeof(*c)); + ZeroMemory(c, sizeof(*c)); + + c->magic = KHUI_NC_MAGIC; + InitializeCriticalSection(&c->cs); + c->result = KHUI_NC_RESULT_CANCEL; + c->mode = KHUI_NC_MODE_MINI; + + *ppnc = c; + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_cw_destroy_cred_blob(khui_new_creds *c) +{ + khm_size i; + size_t len; + EnterCriticalSection(&c->cs); + for(i=0;i<c->n_identities;i++) { + kcdb_identity_release(c->identities[i]); + } + cw_free_prompts(c); + khui_context_release(&c->ctx); + LeaveCriticalSection(&c->cs); + DeleteCriticalSection(&c->cs); + + if(c->password) { + len = wcslen(c->password); + SecureZeroMemory(c->password, sizeof(wchar_t) * len); + free(c->password); + } + + if(c->identities) + free(c->identities); + + if(c->types) + free(c->types); + + if (c->window_title) + free(c->window_title); + + free(c); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_cw_lock_nc(khui_new_creds * c) +{ + EnterCriticalSection(&c->cs); + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_cw_unlock_nc(khui_new_creds * c) +{ + LeaveCriticalSection(&c->cs); + return KHM_ERROR_SUCCESS; +} + +#define NC_N_IDENTITIES 4 + +KHMEXP khm_int32 KHMAPI +khui_cw_add_identity(khui_new_creds * c, + khm_handle id) +{ + if(id == NULL) + return KHM_ERROR_SUCCESS; /* we return success because adding + a NULL id is equivalent to adding + nothing. */ + EnterCriticalSection(&(c->cs)); + + if(c->identities == NULL) { + c->nc_identities = NC_N_IDENTITIES; + c->identities = malloc(sizeof(*(c->identities)) * + c->nc_identities); + c->n_identities = 0; + } else if(c->n_identities + 1 > c->nc_identities) { + khm_handle * ni; + + c->nc_identities = UBOUNDSS(c->n_identities + 1, + NC_N_IDENTITIES, + NC_N_IDENTITIES); + ni = malloc(sizeof(*(c->identities)) * c->nc_identities); + memcpy(ni, c->identities, + sizeof(*(c->identities)) * c->n_identities); + free(c->identities); + c->identities = ni; + } + + kcdb_identity_hold(id); + c->identities[c->n_identities++] = id; + LeaveCriticalSection(&(c->cs)); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_cw_set_primary_id(khui_new_creds * c, + khm_handle id) +{ + khm_size i; + khm_int32 rv; + + EnterCriticalSection(&c->cs); + + /* no change */ + if((c->n_identities > 0 && c->identities[0] == id) || + (c->n_identities == 0 && id == NULL)) { + LeaveCriticalSection(&c->cs); + return KHM_ERROR_SUCCESS; + } + + for(i=0; i<c->n_identities; i++) { + kcdb_identity_release(c->identities[i]); + } + c->n_identities = 0; + + LeaveCriticalSection(&(c->cs)); + rv = khui_cw_add_identity(c,id); + if(c->hwnd != NULL) { + PostMessage(c->hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_IDENTITY_CHANGE), 0); + } + return rv; +} + +KHMEXP khm_int32 KHMAPI +khui_cw_add_type(khui_new_creds * c, + khui_new_creds_by_type * t) +{ + EnterCriticalSection(&c->cs); + + if(c->n_types >= KHUI_MAX_NCTYPES) { + LeaveCriticalSection(&c->cs); + return KHM_ERROR_OUT_OF_BOUNDS; + } + + if(c->types == NULL) { + c->nc_types = CW_ALLOC_INCR; + c->types = malloc(sizeof(*(c->types)) * c->nc_types); + c->type_subs = malloc(sizeof(*(c->type_subs)) * c->nc_types); + c->n_types = 0; + } + + if(c->nc_types < c->n_types + 1) { + void * t; + khm_size n; + + n = UBOUNDSS(c->n_types + 1, CW_ALLOC_INCR, CW_ALLOC_INCR); + + t = malloc(sizeof(*(c->types)) * n); + memcpy(t, (void *) c->types, sizeof(*(c->types)) * c->n_types); + free(c->types); + c->types = t; + + t = malloc(sizeof(*(c->type_subs)) * n); + memcpy(t, (void *) c->type_subs, sizeof(*(c->type_subs)) * c->n_types); + free(c->type_subs); + c->type_subs = t; + + c->nc_types = n; + } + + c->type_subs[c->n_types] = kcdb_credtype_get_sub(t->type); + c->types[c->n_types++] = t; + t->nc = c; + LeaveCriticalSection(&c->cs); + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_cw_del_type(khui_new_creds * c, + khm_int32 type_id) +{ + khm_size i; + + EnterCriticalSection(&c->cs); + for(i=0; i < c->n_types; i++) { + if(c->types[i]->type == type_id) + break; + } + if(i >= c->n_types) { + LeaveCriticalSection(&c->cs); + return KHM_ERROR_NOT_FOUND; + } + c->n_types--; + for(;i < c->n_types; i++) { + c->types[i] = c->types[i+1]; + c->type_subs[i] = c->type_subs[i+1]; + } + LeaveCriticalSection(&c->cs); + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_cw_find_type( + khui_new_creds * c, + khm_int32 type, + khui_new_creds_by_type **t) +{ + khm_size i; + + EnterCriticalSection(&c->cs); + *t = NULL; + for(i=0;i<c->n_types;i++) { + if(c->types[i]->type == type) { + *t = c->types[i]; + break; + } + } + LeaveCriticalSection(&c->cs); + + if(*t) + return KHM_ERROR_SUCCESS; + return KHM_ERROR_NOT_FOUND; +} + + +KHMEXP khm_int32 KHMAPI +khui_cw_enable_type( + khui_new_creds * c, + khm_int32 type, + khm_boolean enable) +{ + khui_new_creds_by_type * t = NULL; + BOOL delta = FALSE; + + EnterCriticalSection(&c->cs); + if(KHM_SUCCEEDED(khui_cw_find_type(c, type, &t))) { + if(enable) { + delta = t->flags & KHUI_NCT_FLAG_DISABLED; + t->flags &= ~KHUI_NCT_FLAG_DISABLED; + } + else { + delta = !(t->flags & KHUI_NCT_FLAG_DISABLED); + t->flags |= KHUI_NCT_FLAG_DISABLED; + } + } + LeaveCriticalSection(&c->cs); + + if(delta) + PostMessage(c->hwnd, KHUI_WM_NC_NOTIFY, MAKEWPARAM(0,WMNC_TYPE_STATE), (LPARAM) type); + + return (t)?KHM_ERROR_SUCCESS:KHM_ERROR_NOT_FOUND; +} + +KHMEXP khm_boolean KHMAPI +khui_cw_type_succeeded( + khui_new_creds * c, + khm_int32 type) +{ + khui_new_creds_by_type * t; + khm_boolean s; + + EnterCriticalSection(&c->cs); + if(KHM_SUCCEEDED(khui_cw_find_type(c, type, &t))) { + s = (t->flags & KHUI_NCT_FLAG_PROCESSED) && !(t->flags & KHUI_NC_RESPONSE_FAILED); + } else { + s = FALSE; + } + LeaveCriticalSection(&c->cs); + + return s; +} + +static khui_new_creds_prompt * +cw_create_prompt(khm_size idx, + khm_int32 type, + wchar_t * prompt, + wchar_t * def, + khm_int32 flags) +{ + khui_new_creds_prompt * p; + size_t cb_prompt; + size_t cb_def; + + if(prompt && FAILED(StringCbLength(prompt, KHUI_MAXCB_PROMPT, &cb_prompt))) + return NULL; + if(def && FAILED(StringCbLength(def, KHUI_MAXCB_PROMPT_VALUE, &cb_def))) + return NULL; + + p = malloc(sizeof(*p)); + ZeroMemory(p, sizeof(*p)); + + if(prompt) { + cb_prompt += sizeof(wchar_t); + p->prompt = malloc(cb_prompt); + StringCbCopy(p->prompt, cb_prompt, prompt); + } + + if(def) { + cb_def += sizeof(wchar_t); + p->def = malloc(cb_def); + StringCbCopy(p->def, cb_def, def); + } + + p->value = malloc(KHUI_MAXCB_PROMPT_VALUE); + ZeroMemory(p->value, KHUI_MAXCB_PROMPT_VALUE); + + p->type = type; + p->flags = flags; + p->index = idx; + + return p; +} + +static void +cw_free_prompt(khui_new_creds_prompt * p) { + size_t cb; + + if(p->prompt) { + if(SUCCEEDED(StringCbLength(p->prompt, KHUI_MAXCB_PROMPT, &cb))) + SecureZeroMemory(p->prompt, cb); + free(p->prompt); + } + + if(p->def) { + if(SUCCEEDED(StringCbLength(p->def, KHUI_MAXCB_PROMPT, &cb))) + SecureZeroMemory(p->def, cb); + free(p->def); + } + + if(p->value) { + if(SUCCEEDED(StringCbLength(p->value, KHUI_MAXCB_PROMPT_VALUE, &cb))) + SecureZeroMemory(p->value, cb); + free(p->value); + } + + free(p); +} + +static void +cw_free_prompts(khui_new_creds * c) +{ + khm_size i; + + if(c->banner != NULL) { + free(c->banner); + c->banner = NULL; + } + + if(c->pname != NULL) { + free(c->pname); + c->pname = NULL; + } + + for(i=0;i < c->n_prompts; i++) { + if(c->prompts[i]) { + cw_free_prompt(c->prompts[i]); + c->prompts[i] = NULL; + } + } + + if(c->prompts != NULL) { + free(c->prompts); + c->prompts = NULL; + } + + c->nc_prompts = 0; + c->n_prompts = 0; +} + +KHMEXP khm_int32 KHMAPI +khui_cw_clear_prompts(khui_new_creds * c) +{ + SendMessage(c->hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0,WMNC_CLEAR_PROMPTS), (LPARAM) c); + + EnterCriticalSection(&c->cs); + cw_free_prompts(c); + LeaveCriticalSection(&c->cs); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_cw_begin_custom_prompts(khui_new_creds * c, + khm_size n_prompts, + wchar_t * banner, + wchar_t * pname) +{ + size_t cb; + + PostMessage(c->hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0,WMNC_CLEAR_PROMPTS), (LPARAM) c); + + EnterCriticalSection(&c->cs); +#ifdef DEBUG + assert(c->n_prompts == 0); +#endif + cw_free_prompts(c); + + if(SUCCEEDED(StringCbLength(banner, KHUI_MAXCB_BANNER, &cb)) && + cb > 0) { + cb += sizeof(wchar_t); + c->banner = malloc(cb); + StringCbCopy(c->banner, cb, banner); + } else { + c->banner = NULL; + } + + if(SUCCEEDED(StringCbLength(pname, KHUI_MAXCB_PNAME, &cb)) && + cb > 0) { + + cb += sizeof(wchar_t); + c->pname = malloc(cb); + StringCbCopy(c->pname, cb, pname); + + } else { + + c->pname = NULL; + + } + + if(n_prompts > 0) { + + c->prompts = malloc(sizeof(*(c->prompts)) * n_prompts); + ZeroMemory(c->prompts, sizeof(*(c->prompts)) * n_prompts); + c->nc_prompts = n_prompts; + c->n_prompts = 0; + + } else { + + c->prompts = NULL; + c->n_prompts = 0; + c->nc_prompts = 0; + + PostMessage(c->hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_SET_PROMPTS), (LPARAM) c); + } + + LeaveCriticalSection(&c->cs); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_cw_add_prompt(khui_new_creds * c, + khm_int32 type, + wchar_t * prompt, + wchar_t * def, + khm_int32 flags) +{ + khui_new_creds_prompt * p; + + if(c->nc_prompts == 0 || + c->n_prompts == c->nc_prompts) + return KHM_ERROR_INVALID_OPERATION; + +#ifdef DEBUG + assert(c->prompts != NULL); +#endif + + EnterCriticalSection(&c->cs); + p = cw_create_prompt(c->n_prompts, type, prompt, def, flags); + if(p == NULL) { + LeaveCriticalSection(&c->cs); + return KHM_ERROR_INVALID_PARM; + } + c->prompts[c->n_prompts++] = p; + LeaveCriticalSection(&c->cs); + + if(c->n_prompts == c->nc_prompts) { + PostMessage(c->hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_SET_PROMPTS), (LPARAM) c); + /* once we are done adding prompts, switch to the auth + panel */ +#if 0 + /* Actually, don't. Doing so can mean an unexpected panel + switch if fiddling on some other panel causes a change in + custom prompts. */ + SendMessage(c->hwnd, KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_DIALOG_SWITCH_PANEL), + (LPARAM) c); +#endif + } + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_cw_get_prompt_count(khui_new_creds * c, + khm_size * np) { + + EnterCriticalSection(&c->cs); + *np = c->n_prompts; + LeaveCriticalSection(&c->cs); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_cw_get_prompt( + khui_new_creds * c, + khm_size idx, + khui_new_creds_prompt ** prompt) +{ + khm_int32 rv; + + EnterCriticalSection(&c->cs); + if(c->n_prompts <= idx || + c->prompts == NULL) { + + rv = KHM_ERROR_OUT_OF_BOUNDS; + *prompt = NULL; + } else { + + *prompt = c->prompts[idx]; + rv = KHM_ERROR_SUCCESS; + } + LeaveCriticalSection(&c->cs); + + return rv; +} + +KHMEXP khm_int32 KHMAPI +khui_cw_sync_prompt_values(khui_new_creds * c) +{ + khm_size i; + + EnterCriticalSection(&c->cs); + for(i=0;i<c->n_prompts;i++) { + khui_new_creds_prompt * p; + p = c->prompts[i]; + if(p->hwnd_edit) { + /* Ideally, we would retrieve the text to a temporary + buffer with the c->cs released, obtain c->cs and copy the + text to p->value. However, I'm not going to bother as the + code paths we are touching here do not need c->cs while + setting p->value does */ + GetWindowText(p->hwnd_edit, p->value, + KHUI_MAXCCH_PROMPT_VALUE); + } + } + LeaveCriticalSection(&c->cs); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_cw_get_prompt_value(khui_new_creds * c, + khm_size idx, + wchar_t * buf, + khm_size *cbbuf) +{ + khui_new_creds_prompt * p; + khm_int32 rv; + size_t cb; + + rv = khui_cw_get_prompt(c, idx, &p); + if(KHM_FAILED(rv)) + return rv; + + EnterCriticalSection(&c->cs); + + if(FAILED(StringCbLength(p->value, KHUI_MAXCB_PROMPT_VALUE, &cb))) { + *cbbuf = 0; + if(buf != NULL) + *buf = 0; + LeaveCriticalSection(&c->cs); + return KHM_ERROR_SUCCESS; + } + cb += sizeof(wchar_t); + + if(buf == NULL || *cbbuf < cb) { + *cbbuf = cb; + LeaveCriticalSection(&c->cs); + return KHM_ERROR_TOO_LONG; + } + + StringCbCopy(buf, *cbbuf, p->value); + *cbbuf = cb; + LeaveCriticalSection(&c->cs); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +khui_cw_set_response(khui_new_creds * c, + khm_int32 type, + khm_int32 response) +{ + khui_new_creds_by_type * t = NULL; + EnterCriticalSection(&c->cs); + khui_cw_find_type(c, type, &t); + c->response |= response & KHUI_NCMASK_RESPONSE; + if(t) { + t->flags &= ~KHUI_NCMASK_RESULT; + t->flags |= (response & KHUI_NCMASK_RESULT); + if (!(response & KHUI_NC_RESPONSE_NOEXIT) && + !(response & KHUI_NC_RESPONSE_PENDING)) + t->flags |= KHUI_NC_RESPONSE_COMPLETED; + } + LeaveCriticalSection(&c->cs); + return KHM_ERROR_SUCCESS; +} + +/* only called from a identity provider callback */ +KHMEXP khm_int32 KHMAPI +khui_cw_add_control_row(khui_new_creds * c, + HWND label, + HWND input, + khui_control_size size) +{ + if (c && c->hwnd) { + khui_control_row row; + + row.label = label; + row.input = input; + row.size = size; + + SendMessage(c->hwnd, + KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0, WMNC_ADD_CONTROL_ROW), + (LPARAM) &row); + + return KHM_ERROR_SUCCESS; + } else { + return KHM_ERROR_INVALID_PARM; + } +} diff --git a/src/windows/identity/uilib/khaction.h b/src/windows/identity/uilib/khaction.h new file mode 100644 index 000000000..7b7c22a57 --- /dev/null +++ b/src/windows/identity/uilib/khaction.h @@ -0,0 +1,566 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_ACTION_H +#define __KHIMAIRA_ACTION_H + +/*! \addtogroup khui + @{*/ +/*! \defgroup khui_actions Actions + @{*/ + +/*! \brief An action */ +typedef struct tag_khui_action { + int cmd; /*!< command id */ + int type; /*!< combination of KHUI_ACTIONTYPE_* */ + wchar_t * name; /*!< name for named actions. NULL if not named. */ + + /* normal, hot and disabled are toolbar sized bitmaps */ + int ib_normal; /*!< normal bitmap (index) */ + int ib_hot; /*!< hot bitmap (index) */ + int ib_disabled; /*!< disabled bitmap (index) */ + + int ib_icon; /*!< index of small (16x16) icon (for menu) */ + int ib_icon_dis; /*!< index of disabled (greyed) icon */ + + int is_caption; /*!< index of string resource for caption */ + int is_tooltip; /*!< same for description / tooltip */ + int ih_topic; /*!< help topic */ + int state; /*!< current state. combination of KHUI_ACTIONSTATE_* */ +} khui_action; + +/*! \brief Unknown action type */ +#define KHUI_ACTIONTYPE_NONE 0 + +/*! \brief A trigger type action */ +#define KHUI_ACTIONTYPE_TRIGGER 1 + +/*! \brief A toggle type action + + A toggle type action typically changes the CHECKED state of the + action each time it is invoked. + */ +#define KHUI_ACTIONTYPE_TOGGLE 2 + +/*! \brief The action is enabled */ +#define KHUI_ACTIONSTATE_ENABLED 0 +/*! \brief The action is diabled */ +#define KHUI_ACTIONSTATE_DISABLED 1 +/*! \brief For toggle type actions, the action is checked */ +#define KHUI_ACTIONSTATE_CHECKED 2 +/*! \brief The action is hot + + Typically this means that the user is hovering the pointing device + over a UI element representing the action. + */ +#define KHUI_ACTIONSTATE_HOT 4 + +#ifdef NOEXPORT +#define ACTION_SIMPLE(c,cap,des,top) \ + {c,KHUI_ACTIONTYPE_TRIGGER,0,0,0,0,0,cap,des,top,0} + +#define ACTION_FULL(cmd,type,inormal,ihot,idis,isml,ismld,capt,toolt,topic,state) \ + {cmd,type,inormal,ihot,idis,isml,ismld,capt,toolt,topic,state} + +#define ACTION_SIMPLE_IMAGE(c,inormal, ihot, idis, isml, ismld,cap, des, top) \ + {c,KHUI_ACTIONTYPE_TRIGGER,inormal,ihot,idis,isml,ismld,cap,des,top,0} +#endif + +/*! \brief A reference to an action */ +typedef struct tag_khui_action_ref { + int flags; + union { + int action; + khui_action * p_action; + }; +} khui_action_ref; + +#define KHUI_ACTIONREF_SUBMENU 0x01 +#define KHUI_ACTIONREF_SEP 0x02 +#define KHUI_ACTIONREF_PACTION 0x04 +#define KHUI_ACTIONREF_FREE_PACTION 0x08 +#define KHUI_ACTIONREF_END 0x10 +#define KHUI_ACTIONREF_DEFAULT 0x20 + +#ifdef NOEXPORT +#define MENU_ACTION(c) {0,c} +#define MENU_DEFACTION(c) {KHUI_ACTIONREF_DEFAULT, c} +#define MENU_SUBMENU(s) {KHUI_ACTIONREF_SUBMENU,s} +#define MENU_SEP() {KHUI_ACTIONREF_SEP,KHUI_MENU_SEP} +#define MENU_END() {KHUI_ACTIONREF_END,KHUI_MENU_END} +#endif + +/*! \brief Menu definition */ +typedef struct tag_khui_menu_def { + int cmd; /*!< Action associated with menu */ + int state; /*!< combination of KHUI_MENUSTATE_* */ + size_t n_items; /*!< total number of items or -1 if not + set. If this is -1, then the list of + actions must be terminated with a + ACTION_LIST_END entry. */ + size_t nc_items; /*!< max number of items in the buffer + alocated for items. Ignored if + KHUI_MENUSTATE_CONSTANT is set in \a + state.*/ + khui_action_ref *items; /*!< Action list terminated by, + ACTION_LIST_END. If \a n_items is set + to a value other than -1, the list + doesn't necessarily have to end with a + ACTION_LIST_END. */ +} khui_menu_def; + +#ifdef NOEXPORT +#define CONSTMENU(c,s,i) {c,s,-1,-1,i} +#endif + +#define KHUI_MENU_END -2 +#define KHUI_MENU_SEP -1 + +#define KHUI_MENUSTATE_CONSTANT 0 +#define KHUI_MENUSTATE_ALLOCD 1 + +/*! \brief Accelerator definition */ +typedef struct tag_khui_accel_def { + int cmd; + int mod; + int key; + int scope; +} khui_accel_def; + +#define KHUI_ACCEL_SCOPE_GLOBAL 0 + +#ifdef NOEXPORT + +extern khui_accel_def khui_accel_global[]; +extern int khui_n_accel_global; + +extern khui_action khui_actions[]; +extern int khui_n_actions; + +extern khui_menu_def khui_all_menus[]; +extern int khui_n_all_menus; + +#endif /* NOEXPORT */ + +/* functions */ + +KHMEXP khui_menu_def * KHMAPI khui_menu_create(int cmd); +KHMEXP khui_menu_def * KHMAPI khui_menu_dup(khui_menu_def * src); +KHMEXP void KHMAPI khui_menu_delete(khui_menu_def * d); +KHMEXP void KHMAPI khui_menu_add_action(khui_menu_def * d, int id); +KHMEXP void KHMAPI khui_menu_add_paction(khui_menu_def * d, khui_action * act, int flags); + +/*! \brief Action scope identifiers + + The scope identifier is a value which describes the scope of the + cursor context. See documentation on individual scope identifiers + for details. + + The role of the scope identifier is to provide a summary of the + current cursor context. Specifically, these identify several + special cases of credential selection, such as the selection of an + entire identity, a credential type or a single credential. If + none of these are applicable, then the generic scope identifier + ::KHUI_SCOPE_GROUP is set or ::KHUI_SCOPE_NONE if there is nothing + selected. + + Note that the scope typically only apply to cursor contexts and + not the selection context. Please see + \ref khui_context "UI Contexts" for more information. + + \see \ref khui_context "UI Contexts" +*/ +typedef enum tag_khui_scope { + KHUI_SCOPE_NONE, + /*!< No context. Nothing is selected. */ + + KHUI_SCOPE_IDENT, + /*!< Identity. The selection is the entire identity specified in + the \a identity field of the context. */ + + KHUI_SCOPE_CREDTYPE, + /*!< A credentials type. The selection is an entire credentials + type. If \a identity is non-NULL, then the scope is all the + credentials of type \a cred_type which belong to \a identity. + Otherwise, the selection is all credentials of type \a + cred_type. + + \note The \a identity can be non-NULL even for the case where + all credentials of type \a cred_type under \a identity is the + same scope as all credentials of type \a cred_type under all + identities. */ + + KHUI_SCOPE_GROUP, + /*!< A grouping of credentials. The scope is a group of + credentials which can not be simplified using one of the other + context identifiers. The \a headers array contains \a n_headers + elements describing the outline level that has been selected. + + \see ::khui_header + \see \ref khui_context_sel_ctx_grp "KHUI_SCOPE_GROUP description" */ + + KHUI_SCOPE_CRED + /*!< A single credential. Only a single credential was + selected. The \a cred field of the context specifies the + credential. The \a identity and \a cred_type fields specify the + identity and the credential type respectively. */ +} khui_scope; + + +/*! \brief Outline header + + Describes an outline header in the user interface. + + \see \ref khui_context_sel_ctx_grp "KHUI_SCOPE_GROUP description" + */ +typedef struct tag_khui_header { + khm_int32 attr_id; /*!< Attribute ID */ + void * data; /*!< Value of attribute */ + khm_size cb_data; /*!< Size of the value */ +} khui_header; + +/*! \brief Maximum number of outline headers + + This is the maximum number of fields that the credentials view can + be grouped by. + */ +#define KHUI_MAX_HEADERS 6 + +/*! \brief Action context + + Represents the UI context for an action. + */ +typedef struct tag_khui_action_context { + khm_int32 magic; /*!< Internal. */ + khui_scope scope; /*!< Context scope. One of ::khui_scope*/ + khm_handle identity; /*!< Identity */ + khm_int32 cred_type; /*!< Credential type ID */ + khm_handle cred; /*!< Credential */ + + khui_header headers[KHUI_MAX_HEADERS]; + /*!< The ordered set of outline + headers which define the current + cursor location. */ + + khm_size n_headers; /*!< Number of actual headers defined + above */ + + khm_handle credset; /*!< Handle to a credential set + containing the currently selected + credentials. When the context is + obtained through khui_context_get(), + this credential is returned in a + sealed state. */ + + khm_size n_sel_creds; /*!< Number of selected credentials */ + + void * int_buf; /*!< Internal. Do not use. */ + khm_size int_cb_buf; /*!< Internal. Do not use. */ + khm_size int_cb_used; /*!< Internal. Do not use. */ + + void * vparam; /*!< Optional data */ + khm_size cb_vparam; /*!< Size of optional data */ +} khui_action_context; + +/*! \brief Set the current context + + Changes the UI context to that represented by the parameters to + the function. Note that specifying a valid \a identity or \a cred + parameter will result in an automatic hold on the respective + object. The hold will stay until another call to + khui_context_set() overwrites the identity or credential handle or + a call to khui_context_reset() is made. + + While this API is available, it is only called from the main + NetIDMgr application. Plugins do not have a good reason to call + this API directly and should not do so. + + \param[in] scope The new context scope + + \param[in] identity A handle to an identity. If this is not NULL, + then it should be a valid handle to an identity. Required if + \a scope specifies ::KHUI_SCOPE_IDENT. Optional if \a scope + specifies ::KHUI_SCOPE_CREDTYPE. Ignored otherwise. + + \param[in] cred_type A credentials type. Specify + ::KCDB_CREDTYPE_INVALID if this parameter is not given or not + relevant. Required if \a scope specifies + ::KHUI_SCOPE_CREDTYPE. Ignored otherwise. + + \param[in] cred A handle to a credential. If this parameter is + not NULL it is expected to be a valid handle to a credential. + Required if \a scope specifies ::KHUI_SCOPE_CRED. Ignored + otherwise. + + \param[in] headers An array of headers. The \a n_headers + parameter specifies the number of elements in the array. Set + to NULL if not specified. Required if \a scope specifies + ::KHUI_SCOPE_GROUP. + + \param[in] n_headers Number of elements in \a headers. Must be + less than or equal to ::KHUI_MAX_HEADERS. Required if \a + headers is not NULL. Ignored otherwise. + + \param[in] cs_src A handle to a credential set from which the + selected credentials will be extracted. The credentials that + are selected must have the ::KCDB_CRED_FLAG_SELECTED flag set. + + \note This function should only be called from the UI thread. + */ +KHMEXP void KHMAPI +khui_context_set(khui_scope scope, + khm_handle identity, + khm_int32 cred_type, + khm_handle cred, + khui_header *headers, + khm_size n_headers, + khm_handle cs_src); + +/*! \brief Set the current context + + Changes the UI context to that represented by the parameters to + the function. Note that specifying a valid \a identity or \a cred + parameter will result in an automatic hold on the respective + object. The hold will stay until another call to + khui_context_set() overwrites the identity or credential handle or + a call to khui_context_reset() is made. + + While this API is available, it is only called from the main + NetIDMgr application. Plugins do not have a good reason to call + this API directly and should not do so. + + \param[in] scope The new context scope + + \param[in] identity A handle to an identity. If this is not NULL, + then it should be a valid handle to an identity. Required if + \a scope specifies ::KHUI_SCOPE_IDENT. Optional if \a scope + specifies ::KHUI_SCOPE_CREDTYPE. Ignored otherwise. + + \param[in] cred_type A credentials type. Specify + ::KCDB_CREDTYPE_INVALID if this parameter is not given or not + relevant. Required if \a scope specifies + ::KHUI_SCOPE_CREDTYPE. Ignored otherwise. + + \param[in] cred A handle to a credential. If this parameter is + not NULL it is expected to be a valid handle to a credential. + Required if \a scope specifies ::KHUI_SCOPE_CRED. Ignored + otherwise. + + \param[in] headers An array of headers. The \a n_headers + parameter specifies the number of elements in the array. Set + to NULL if not specified. Required if \a scope specifies + ::KHUI_SCOPE_GROUP. + + \param[in] n_headers Number of elements in \a headers. Must be + less than or equal to ::KHUI_MAX_HEADERS. Required if \a + headers is not NULL. Ignored otherwise. + + \param[in] cs_src A handle to a credential set from which the + selected credentials will be extracted. The credentials that + are selected must have the ::KCDB_CRED_FLAG_SELECTED flag set. + + \param[in] vparam Optional parameter blob + + \param[in] cb_vparam Size of parameter blob + + \note This function should only be called from the UI thread. + */ +KHMEXP void KHMAPI +khui_context_set_ex(khui_scope scope, + khm_handle identity, + khm_int32 cred_type, + khm_handle cred, + khui_header *headers, + khm_size n_headers, + khm_handle cs_src, + void * vparam, + khm_size cb_vparam); + +/*! \brief Obtain the current UI context + + The parameter specified by \a ctx will receive the current UI + context. If the context contains an identity or a credential + handle, a hold will be obtained on the relevant object. Use + khui_context_release() to release the holds obtained in a prior + call to khui_context_get(). + + \note The returned context should not be modified prior to calling + khui_context_release(). +*/ +KHMEXP void KHMAPI +khui_context_get(khui_action_context * ctx); + +/*! \brief Create a new UI context + + The created context does not have any relation to the current UI + context. This function is provided for use in situations where an + application needs to provide a scope description through a + ::khui_action_context structure. + + Once the application is done with the context, it should call + khui_context_release() to release the created context. + */ +KHMEXP void KHMAPI +khui_context_create(khui_action_context * ctx, + khui_scope scope, + khm_handle identity, + khm_int32 cred_type, + khm_handle cred); + +/*! \brief Release a context obtained using khui_context_get() + + Releases all holds obtained on related objects in a prior call to + khui_context_get() and nullifies the context. + + \note The context should not have been modified between calling + khui_context_get() and khui_context_release() + */ +KHMEXP void KHMAPI +khui_context_release(khui_action_context * ctx); + +/*! \brief Reset the UI context + + Nullifies the current UI context and releases any holds obtained + on objects related to the previous context. +*/ +KHMEXP void KHMAPI +khui_context_reset(void); + +/*! \brief Refresh context data + + Setting the UI context involves other side effects such as + activation of or disabling certain actions based on the selection. + If an operation is performed which may affect the side effects, + khui_context_refresh() is called to refresh them. + + An example is when setting the default identity. The state of the + action ::KHUI_ACTION_SET_DEF_ID depends on whether the currently + selected identity is the default. However, if the currently + selected identity becomes the default after selection, then + khui_context_refresh() should be called to adjust the state of the + ::KHUI_ACTION_SET_DEF_ID action. + */ +KHMEXP void KHMAPI +khui_context_refresh(void); + +/*! \brief A filter function that filters for credentials in the cursor context + + This is a function of type ::kcdb_cred_filter_func which can be + used to filter for credentials that are included in the cursor + context. + + The \a rock parameter should be a pointer to a + ::khui_action_context structure which will be used as the filter. + + For example, the following code will extract the cursor context + credentials into the credential set \a my_credset based on the UI + context \a my context: + + \code + kcdb_credset_extract_filtered(my_credset, + NULL, + khui_context_cursor_filter, + (void *) my_context); + \endcode +*/ +KHMEXP khm_int32 KHMAPI +khui_context_cursor_filter(khm_handle cred, + khm_int32 flags, + void * rock); + +/*! \brief Get a string representation of an accelerator + + \param[in] cmd Command for which to obtain the accelerator string for + \param[out] buf Buffer to receive the accelerator string + \param[in] bufsiz Size of the buffer in bytes. Note that the size of the + buffer must be sufficient to hold at least one character and a + NULL terminator. + + \return TRUE if the operation was successful. FALSE otherwise. + */ +KHMEXP khm_boolean KHMAPI khui_get_cmd_accel_string(int cmd, wchar_t * buf, size_t bufsiz); + +KHMEXP HACCEL KHMAPI khui_create_global_accel_table(void); + +/*! \brief Find a menu by id */ +KHMEXP khui_menu_def * KHMAPI khui_find_menu(int id); + +/*! \brief Find an action by id */ +KHMEXP khui_action * KHMAPI khui_find_action(int id); + +/*! \brief Get the length of the action list */ +KHMEXP size_t KHMAPI khui_action_list_length(khui_action_ref * ref); + +/*! \brief Find an action by name */ +KHMEXP khui_action * KHMAPI khui_find_named_action(wchar_t * name); + +/*! \brief Enables or disables a group of actions + + The group of actions are specified by the menu definition. All + valid action entries in the menu are marked as enabled or disabled + according to the value of \a enable. + */ +KHMEXP void KHMAPI khui_enable_actions(khui_menu_def * d, khm_boolean enable); + +/*! \brief Enables or disables an action + + The action designated by the command \a cmd will either be enabled + or disabled depending on the \a enable parameter. If \a enable is + TRUE then the action is enabled. + */ +KHMEXP void KHMAPI khui_enable_action(int cmd, khm_boolean enable); + +/*! \brief Check an action in an action group + + Marks the action denoted by \a cmd as checked and resets the + checked bit in all other actions. + + \param[in] d A menu definition. + \param[in] cmd A command identifier. Setting this to -1 will + reset the checked bit in all the actions in the menu + definition. + */ +KHMEXP void KHMAPI khui_check_radio_action(khui_menu_def * d, khm_int32 cmd); + +/*!\cond INTERNAL */ + +/*! \brief Initialize actions + + \note Only called by the NetIDMgr application + */ +KHMEXP void KHMAPI khui_init_actions(void); + +/*! \brief Exit actions + + \note Only called by the NetIDMgr application + */ +KHMEXP void KHMAPI khui_exit_actions(void); + +/*! \endcond */ + +/*@}*/ +/*@}*/ +#endif diff --git a/src/windows/identity/uilib/khactiondef.h b/src/windows/identity/uilib/khactiondef.h new file mode 100644 index 000000000..d01eb2add --- /dev/null +++ b/src/windows/identity/uilib/khactiondef.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_ACTIONDEF_H +#define __KHIMAIRA_ACTIONDEF_H + +/*! \ingroup khui_actions + @{*/ +/*! \defgroup khui_std_actions Standard Actions +@{ */ + +/*!\name Standard actions + @{*/ +#define KHUI_ACTION_BASE 50000 + +#define KHUI_ACTION_PROPERTIES (KHUI_ACTION_BASE + 0) +#define KHUI_ACTION_EXIT (KHUI_ACTION_BASE + 1) +#define KHUI_ACTION_SET_DEF_ID (KHUI_ACTION_BASE + 3) +#define KHUI_ACTION_SET_SRCH_ID (KHUI_ACTION_BASE + 4) +#define KHUI_ACTION_PASSWD_ID (KHUI_ACTION_BASE + 7) +#define KHUI_ACTION_NEW_CRED (KHUI_ACTION_BASE + 8) +#define KHUI_ACTION_CHOOSE_COLS (KHUI_ACTION_BASE + 9) +#define KHUI_ACTION_DEBUG_WINDOW (KHUI_ACTION_BASE + 10) +#define KHUI_ACTION_VIEW_REFRESH (KHUI_ACTION_BASE + 11) +#define KHUI_ACTION_LAYOUT_ID (KHUI_ACTION_BASE + 12) +#define KHUI_ACTION_LAYOUT_TYPE (KHUI_ACTION_BASE + 13) +#define KHUI_ACTION_LAYOUT_LOC (KHUI_ACTION_BASE + 14) +#define KHUI_ACTION_TB_STANDARD (KHUI_ACTION_BASE + 15) +#define KHUI_ACTION_OPT_KHIM (KHUI_ACTION_BASE + 16) +#define KHUI_ACTION_OPT_IDENTS (KHUI_ACTION_BASE + 17) +#define KHUI_ACTION_OPT_NOTIF (KHUI_ACTION_BASE + 18) +#define KHUI_ACTION_HELP_CTX (KHUI_ACTION_BASE + 19) +#define KHUI_ACTION_HELP_CONTENTS (KHUI_ACTION_BASE + 20) +#define KHUI_ACTION_HELP_INDEX (KHUI_ACTION_BASE + 21) +#define KHUI_ACTION_HELP_ABOUT (KHUI_ACTION_BASE + 22) +#define KHUI_ACTION_DESTROY_CRED (KHUI_ACTION_BASE + 23) +#define KHUI_ACTION_RENEW_CRED (KHUI_ACTION_BASE + 24) +#define KHUI_ACTION_OPEN_APP (KHUI_ACTION_BASE + 25) +#define KHUI_ACTION_MENU_ACTIVATE (KHUI_ACTION_BASE + 26) +#define KHUI_ACTION_CLOSE_APP (KHUI_ACTION_BASE + 27) +#define KHUI_ACTION_IMPORT (KHUI_ACTION_BASE + 28) +/*@}*/ + +/*! \name Pseudo actions + +Pseudo actions do not trigger any specific function, but acts as a +signal of some generic event which will be interpreted based on +context. + +@{*/ +#define KHUI_PACTION_BASE (KHUI_ACTION_BASE + 500) + +#define KHUI_PACTION_MENU (KHUI_PACTION_BASE + 0) +#define KHUI_PACTION_UP (KHUI_PACTION_BASE + 1) +#define KHUI_PACTION_DOWN (KHUI_PACTION_BASE + 2) +#define KHUI_PACTION_LEFT (KHUI_PACTION_BASE + 3) +#define KHUI_PACTION_RIGHT (KHUI_PACTION_BASE + 4) +#define KHUI_PACTION_ENTER (KHUI_PACTION_BASE + 5) +#define KHUI_PACTION_ESC (KHUI_PACTION_BASE + 6) +#define KHUI_PACTION_OK (KHUI_PACTION_BASE + 7) +#define KHUI_PACTION_CANCEL (KHUI_PACTION_BASE + 8) +#define KHUI_PACTION_CLOSE (KHUI_PACTION_BASE + 9) +#define KHUI_PACTION_DELETE (KHUI_PACTION_BASE + 10) +#define KHUI_PACTION_UP_EXTEND (KHUI_PACTION_BASE + 11) +#define KHUI_PACTION_UP_TOGGLE (KHUI_PACTION_BASE + 12) +#define KHUI_PACTION_DOWN_EXTEND (KHUI_PACTION_BASE + 13) +#define KHUI_PACTION_DOWN_TOGGLE (KHUI_PACTION_BASE + 14) +#define KHUI_PACTION_BLANK (KHUI_PACTION_BASE + 15) +/*@}*/ + +/*! \name Menus + +Stock menus. + +@{*/ +#define KHUI_MENU_BASE (KHUI_ACTION_BASE + 1000) + +#define KHUI_MENU_MAIN (KHUI_MENU_BASE + 0) +#define KHUI_MENU_FILE (KHUI_MENU_BASE + 1) +#define KHUI_MENU_CRED (KHUI_MENU_BASE + 2) +#define KHUI_MENU_VIEW (KHUI_MENU_BASE + 3) +#define KHUI_MENU_OPTIONS (KHUI_MENU_BASE + 4) +#define KHUI_MENU_HELP (KHUI_MENU_BASE + 5) + +#define KHUI_MENU_LAYOUT (KHUI_MENU_BASE + 6) +#define KHUI_MENU_TOOLBARS (KHUI_MENU_BASE + 7) + +#define KHUI_MENU_IDENT_CTX (KHUI_MENU_BASE + 8) +#define KHUI_MENU_TOK_CTX (KHUI_MENU_BASE + 9) +#define KHUI_MENU_ICO_CTX_MIN (KHUI_MENU_BASE + 12) +#define KHUI_MENU_ICO_CTX_NORMAL (KHUI_MENU_BASE + 13) + +#define KHUI_PMENU_TOK_SEL (KHUI_MENU_BASE + 10) +#define KHUI_PMENU_ID_SEL (KHUI_MENU_BASE + 11) + +/* Next menu: 14 */ +/*@}*/ + +/*! \name Toolbars +@{*/ +#define KHUI_TOOLBAR_BASE (KHUI_ACTION_BASE + 2000) + +#define KHUI_TOOLBAR_STANDARD (KHUI_TOOLBAR_BASE + 0) +/*@}*/ + +/* base for user actions */ +#define KHUI_USERACTION_BASE (KHUI_ACTION_BASE + 10000) +/*@}*/ +/*@}*/ + +#endif diff --git a/src/windows/identity/uilib/khalerts.h b/src/windows/identity/uilib/khalerts.h new file mode 100644 index 000000000..c41dddf1f --- /dev/null +++ b/src/windows/identity/uilib/khalerts.h @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KHALERTS_H +#define __KHIMAIRA_KHALERTS_H + +/********************************************************************* + Alerter and error reporting +**********************************************************************/ + +/*! \addtogroup khui +@{ */ + +/*!\defgroup khui_alert Alerter and Error Reporting +@{*/ + +#define KHUI_MAX_ALERT_COMMANDS 4 + +/*! \brief An alert + + Describes an alert message that will be shown to the user in a + variety of ways depending on the state of the NetIDMgr + application. + */ +typedef struct tag_khui_alert { + khm_int32 magic; /*!< Magic number. Always set to + KHUI_ALERT_MAGIC */ + + khm_int32 severity; /*!< The severity of the alert. One + of KHERR_ERROR, KHERR_WARNING or + KHERR_INFO. The default is + KHERR_INFO. Do not set directly. + Use khui_alert_set_severity(). */ + + khm_int32 alert_commands[KHUI_MAX_ALERT_COMMANDS]; + /*!< The command buttons associated + with the alert. Use + khui_alert_add_command() to add a + command. The buttons will appear in + the order in which they were added. + The first button will be the + default. Each command should be a + known action identifier. */ + khm_int32 n_alert_commands; + + wchar_t * title; /*!< The title of the alert. Subject + to ::KHUI_MAXCCH_TITLE. Use + khui_alert_set_title() to set. Do + not modify directly. */ + + wchar_t * message; /*!< The main message of the alert. + Subject to ::KHUI_MAXCCH_MESSAGE. + Use khui_alert_set_message() to + set. Do not modify direcly. */ + + wchar_t * suggestion; /*!< A suggestion. Appears below + the message text. Use + khui_alert_set_suggestion() to + set. Do not modify directly. */ + +#ifdef _WIN32 + POINT target; +#endif + + khm_int32 flags; /*!< combination of + ::khui_alert_flags. Do not modify + directly. */ + + kherr_context * err_context; + /*!< If non-NULL at the time the alert + window is shown, this indicates that + the alert window should provide an + error viewer for the given error + context. */ + + kherr_event * err_event; + /*!< If non-NULL at the time the alert + window is shown, this indicates that + the alert window should provide an + error viewer for the given error + event. If an \a err_context is also + given, the error viewer for the + context will be below this error. */ + + khm_int32 response; + /*!< Once the alert is displayed to + the user, when the user clicks one + of the command buttons, the command + ID will be assigned here. */ + + int refcount; /* internal */ + + LDCL(struct tag_khui_alert); /* internal */ +} khui_alert; + +#define KHUI_ALERT_MAGIC 0x48c39ce9 + +/*! \brief Maximum number of characters in title including terminating NULL + */ +#define KHUI_MAXCCH_TITLE 256 + +/*! \brief Maximum number of bytes in title including terminating NULL + */ +#define KHUI_MAXCB_TITLE (KHUI_MAXCCH_TITLE * sizeof(wchar_t)) + +/*! \brief Maximum number of characters in message including terminating NULL + */ +#define KHUI_MAXCCH_MESSAGE 1024 + +/*! \brief Maximum number of bytes in message including terminating NULL + */ +#define KHUI_MAXCB_MESSAGE (KHUI_MAXCCH_MESSAGE * sizeof(wchar_t)) + +/*! \brief Maxumum number of characters in a suggestion including terminating NULL */ +#define KHUI_MAXCCH_SUGGESTION 1024 + +/*! \brief Maximum number of bytes in a suggestion, including terminating NULL */ +#define KHUI_MAXCB_SUGGESTION (KHUI_MAXCCH_SUGGESTION * sizeof(wchar_t)) + +/*! \brief Flags for an alert */ +enum khui_alert_flags { + KHUI_ALERT_FLAG_FREE_STRUCT =0x00000001, + /*!< Internal. Free the structure once the alert is done. */ + + KHUI_ALERT_FLAG_FREE_TITLE =0x00000002, + /*!< Internal. Free the \a title field when the alert is done.*/ + + KHUI_ALERT_FLAG_FREE_MESSAGE =0x00000004, + /*!< Internal. Free the \a message field when the alert is done. */ + + KHUI_ALERT_FLAG_FREE_SUGGEST =0x00000008, + /*!< Internal. Free the \a suggest field when the alert is done */ + + KHUI_ALERT_FLAG_DEFACTION =0x00000010, + /*!< If the message is displayed as a balloon prompt, then perform + the default action when it is clicked. The default action is + the first action added to the alert. Cannot be used if there + are no actions or if ::KHUI_ALERT_FLAG_REQUEST_WINDOW is + specified.*/ + + KHUI_ALERT_FLAG_VALID_TARGET =0x00010000, + /*!< There is a valid target for the alert */ + + KHUI_ALERT_FLAG_VALID_ERROR =0x00020000, + /*!< There is a valid error context associated with the alert */ + + KHUI_ALERT_FLAG_DISPLAY_WINDOW =0x01000000, + /*!< The alert has been displayed in a window */ + + KHUI_ALERT_FLAG_DISPLAY_BALLOON =0x02000000, + /*!< The alert has been displayed in a ballon */ + + KHUI_ALERT_FLAG_REQUEST_WINDOW =0x04000000, + /*!< The alert should be displayed in a window */ + + KHUI_ALERT_FLAG_REQUEST_BALLOON =0x08000000, + /*!< The alert should be displayed in a balloon */ + + KHUI_ALERT_FLAGMASK_RDWR =0x0C000010, + /*!< Bit mask of flags that can be set by khui_alert_set_flags() */ +}; + +/*! \brief Create an empty alert object + + The returned result is a held pointer to a ::khui_alert object. + Use khui_alert_release() to release the object. + */ +KHMEXP khm_int32 KHMAPI +khui_alert_create_empty(khui_alert ** result); + +/*! \brief Create a simple alert object + + The returned result is a held pointer to a ::khui_alert object. + Use khui_alert_release() to release the object. + + \param[in] title The title of the alert. (Required, Localized) + Limited by ::KHUI_MAXCCH_TITLE. + + \param[in] message The message. (Required. Localized). Limited + by ::KHUI_MAXCCH_MESSAGE. + + \param[in] severity One of ::tag_kherr_severity + + \param[out] result Receives a held pointer to a ::khui_alert + object upon successful completion. + */ +KHMEXP khm_int32 KHMAPI +khui_alert_create_simple(const wchar_t * title, + const wchar_t * message, + khm_int32 severity, + khui_alert ** result); + +/*! \brief Set the title of an alert object + + The title is limited by ::KHUI_MAXCCH_TITLE. + */ +KHMEXP khm_int32 KHMAPI +khui_alert_set_title(khui_alert * alert, + const wchar_t * title); + +/*! \brief Set the message of an alert object + + The message is limited by ::KHUI_MAXCCH_MESSAGE. + */ +KHMEXP khm_int32 KHMAPI +khui_alert_set_message(khui_alert * alert, + const wchar_t * message); + +/*! \brief Set the suggestion of an alert object + + The suggestion is limited by ::KHUI_MAXCCH_SUGGESTION + */ +KHMEXP khm_int32 KHMAPI +khui_alert_set_suggestion(khui_alert * alert, + const wchar_t * suggestion); + +/*! \brief Set the severity of the alert object + + The severity value is one of ::tag_kherr_severity + */ +KHMEXP khm_int32 KHMAPI +khui_alert_set_severity(khui_alert * alert, + khm_int32 severity); + +/*! \brief Sets the flags of the alert + + The flags are as defined in ::khui_alert_flags. The bits that are + on in \a mask will be set to the corresponding values in \a flags. + Only the bits specified in ::KHUI_ALERT_FLAGMASK_RDWR can be + specified in \a mask. + */ +KHMEXP khm_int32 KHMAPI +khui_alert_set_flags(khui_alert * alert, khm_int32 mask, khm_int32 flags); + +/*! \brief Clear all the commands from an alert object + + \see khui_alert_add_command() + */ +KHMEXP khm_int32 KHMAPI +khui_alert_clear_commands(khui_alert * alert); + +/*! \brief Add a command to an alert object + + The command ID should be a valid registered command. + */ +KHMEXP khm_int32 KHMAPI +khui_alert_add_command(khui_alert * alert, + khm_int32 command_id); + +/*! \brief Display an alert + + The alert must have a valid \a severity, \a title and a \a message + to be displayed. Otherwise the function immediately returns with + a failure code. + + The method used to display the alert is as follows: + + - A balloon alert will be shown if one of the following is true: + - The NetIDMgr application is minimized or in the background. + - ::KHUI_ALERT_FLAG_REQUEST_BALLOON is specified in \a flags. + - Otherwise an alert window will be shown. + + If the message, title of the alert is too long to fit in a balloon + prompt, there's a suggestion or if there are custom commands then + a placeholder balloon prompt will be shown which when clicked on, + shows the actual alert in an alert window. + + An exception is when ::KHUI_ALERT_FLAG_DEFACTION is specified in + flags. In this case instead of a placeholder balloon prompt, one + will be shown with the actual title and message (truncated if + necessary). Clicking on the balloon will have the same effect as + choosing the first command in the action. + + The placeholder balloon prompt will have a title derived from the + first 63 characters of the \a title field in the alert and a + message notifying the user that they should click the balloon + prompt for more information. + + To this end, it is beneficial to limit the length of the title to + 63 characters (64 counting the terminating NULL). This limit is + enforced on Windows. Also, try to make the title descriptive. + */ +KHMEXP khm_int32 KHMAPI +khui_alert_show(khui_alert * alert); + +/*! \brief Display a simple alert + + \see khui_alert_show() + */ +KHMEXP khm_int32 KHMAPI +khui_alert_show_simple(const wchar_t * title, + const wchar_t * message, + khm_int32 severity); + +/*! \brief Obtain a hold on the alert + + An alert structure is only considered valid for the duration that + there is a hold on it. + + Use khui_alert_release() to release the hold. + */ +KHMEXP khm_int32 KHMAPI +khui_alert_hold(khui_alert * alert); + +/*! \brief Release the hold on the alert + + Holds obtained on an alert using any of the functions that either + return a held pointer to an alert or implicitly obtains a hold on + it need to be undone through a call to khui_alert_release(). + */ +KHMEXP khm_int32 KHMAPI +khui_alert_release(khui_alert * alert); + +/*! \brief Lock an alert + + Locking an alert disallows any other thread from accessing the + alert at the same time. NetIDMgr keeps a global list of all alert + objects and the user interface may access any of them at various + points in time. Locking the alert allows a thread to modify an + alert without causing another thread to be exposed to an + inconsistent state. + + Once a thread obtains a lock on the alert, it must call + khui_alert_unlock() to unlock it. Otherwise no other thread will + be able to access the alert. + + \note Currently the alert lock is global. Locking one alert + disallows access to all other alerts as well. + + \note Calling khui_alert_lock() is only necessary if you are + modifying the ::khui_alert structure directly. Calling any of + the khui_alert_* functions to modify the alert does not + require obtaining a lock, as they perform synchronization + internally. +*/ +KHMEXP void KHMAPI +khui_alert_lock(khui_alert * alert); + +/*! \brief Unlock an alert + + \see khui_alert_lock() +*/ +KHMEXP void KHMAPI +khui_alert_unlock(khui_alert * alert); + +/*!@}*/ +/*!@}*/ + +#endif diff --git a/src/windows/identity/uilib/khconfigui.h b/src/windows/identity/uilib/khconfigui.h new file mode 100644 index 000000000..9a96d41f9 --- /dev/null +++ b/src/windows/identity/uilib/khconfigui.h @@ -0,0 +1,588 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KHCONFIGUI_H +#define __KHIMAIRA_KHCONFIGUI_H + +/*! \addtogroup khui +@{ */ + +/*! \defgroup khui_cfg Configuration Panels + + Configuration panels are the primary means from which the user is + presented with an interface to change NetIDMgr and plugin + configuration. + +@{ */ + +/*! \brief Configuration window notification message + + This is the message that will be used to notify dialog panels. + + The format of the message is : + - uMsg : KHUI_WM_CFG_NOTIFY + - HIWORD(wParam) : one of ::khui_wm_cfg_notifications + + \note This is the same as ::KHUI_WM_NC_NOTIFY + */ +#define KHUI_WM_CFG_NOTIFY (WM_APP + 0x101) + +/*! \brief Configuration notifications + + These are sent thorugh a ::KHUI_WM_CFG_NOTIFY message. + + The format of the message is : + - uMsg : KHUI_WM_CFG_NOTIFY + - HIWORD(wParam) : one of ::khui_wm_cfg_notifications + */ +enum khui_wm_cfg_notifications { + WMCFG_SHOW_NODE = 1, + /*!< Sent to the configuration dialog to request that the panel + for the specified node be shown. The \a lParam message + parameter will contain a held ::khui_config_node handle. The + sender of the mssage is responsible for releasing the handle.*/ + + WMCFG_UPDATE_STATE = 2, + /*!< Sent to the configuration dialog to indicate that the state + flags for the specified configuration node have changed. + + - LOWORD(wParam) : new flags + - lParam : ::khui_config_node for the node*/ + + WMCFG_APPLY = 3, + /*!< Sent to all the configuration panels when the user clicks the + 'Apply' button or the 'Ok' button. The panels are responsible + for applying the configuration changes and updating their flags + using khui_cfg_set_flags(). */ +}; + +/*! \brief Registration information for a configuration node + + \see khui_cfg_register_node() +*/ +typedef struct tag_khui_config_node_reg { + const wchar_t * name; /*!< Internal identifier + (not-localized, required). The name + is required to be unique among + sibling nodes. However it is not + required to be unique globally. The + size of the name is constrained by + ::KHUI_MAXCCH_NAME*/ + + const wchar_t * short_desc; /*!< Short description (Localized, + required). This is the name which + identifies the node within a + collection of siblings. The size of + the string is constrained by + ::KHUI_MAXCCH_SHORT_DESC*/ + + const wchar_t * long_desc; /*!< Global name of the node. + (Localized, required). This + uniquely identifies the node in the + collection of all configuration + nodes. The size of the string is + constrained by + ::KHUI_MAXCCH_LONG_DESC.*/ + + HMODULE h_module; /*!< Module which contains the dialog + resource specified in \a + dlg_template */ + + LPWSTR dlg_template; /*!< Dialog template for the + configuration window */ + + DLGPROC dlg_proc; /*!< Dialog procedure */ + + khm_int32 flags; /*!< Flags. Can be a combination of + ::KHUI_CNFLAG_SORT_CHILDREN and + ::KHUI_CNFLAG_SUBPANEL*/ + +} khui_config_node_reg; + +/*! \brief Sort the child nodes by short description */ +#define KHUI_CNFLAG_SORT_CHILDREN 0x0001 + +/*! \brief Is a subpanel */ +#define KHUI_CNFLAG_SUBPANEL 0x0002 + +/*! \brief Node represents a panel that is replicated for all child nodes */ +#define KHUI_CNFLAG_PLURAL 0x0004 + +#define KHUI_CNFLAG_MODIFIED 0x0010 +#define KHUI_CNFLAG_APPLIED 0x0020 + +#define KHUI_CNFLAGMASK_STATIC 0x000f +#define KHUI_CNFLAGMASK_DYNAMIC 0x00f0 + +/*! \brief Maximum length of the name in characters + + The length includes the terminating NULL + */ +#define KHUI_MAXCCH_NAME 256 + +/*! \brief Maximum length of the name in bytes + + The length includes the terminating NULL + */ +#define KHUI_MAXCB_NAME (KHUI_MAXCCH_NAME * sizeof(wchar_t)) + +/*! \brief Maximum length of the long description in characters + + The length includes the terminating NULL + */ +#define KHUI_MAXCCH_LONG_DESC 1024 + +/*! \brief Maximum length of the long description in bytes + + The length includes the terminating NULL + */ +#define KHUI_MAXCB_LONG_DESC (KHUI_MAXCCH_LONG_DESC * sizeof(wchar_t)) + +/*! \brief Maximum length of the short description in chracters + + The length includes the terminating NULL + */ +#define KHUI_MAXCCH_SHORT_DESC 256 + +/*! \brief Maximum length of the short description in bytes + + The length includes the terminating NULL + */ +#define KHUI_MAXCB_SHORT_DESC (KHUI_MAXCCH_SHORT_DESC * sizeof(wchar_t)) + +/*! \brief Width of a configuration dialog in dialog units + + ::CFGDLG_WIDTH and ::CFGDLG_HEIGHT specify the dimensions of a + configuration dialog width and height in dialog units. The dialog + will be created as a child of the configuration dialog and placed + within it. + */ +#define CFGDLG_WIDTH 255 + +/*! \brief Height of a configuration dialog in dialog units + + \see ::CFGDLG_WIDTH +*/ +#define CFGDLG_HEIGHT 182 + +/*! \brief Width of a configuration tab dialog in dialog units + + ::CFGDLG_TAB_WIDTH and ::CFGDLG_TAB_HEIGHT specify the dimensions + (in dialog units) of a dialog that will be placed within a tab + control for dialogs where multiple display panels need to be + shown. + */ +#define CFGDLG_TAB_WIDTH 235 + +/*! \brief Height of configuration tab dialog in dialog units + + \see ::CFGDLG_TAB_WIDTH + */ +#define CFGDLG_TAB_HEIGHT 151 + +/*! \brief A handle to a configuration node + + \see khui_cfg_open_node(), khui_cfg_close_node() +*/ +typedef khm_handle khui_config_node; + +/*! \brief Initialization data passed in to a subpanel + + When creating a subpanel, a pointer to the following strucutred + will be passed in as the creation parameter for the dialog. +*/ +typedef struct tag_khui_config_init_data { + khui_config_node ctx_node; /*!< The node under which the current + dialog subpanel is being created. */ + + khui_config_node this_node; /*!< The node which provided the + registration information for the + creation of the subpanel. */ + + khui_config_node ref_node; /*!< The parent node of the subpanel + node. In nodes which have the + ::KHUI_CNFLAG_PLURAL, this would be + different from the \a node. This is + the node under which the subpanel + was registered. */ +} khui_config_init_data; + +/*! \brief Register a configuration node + + The caller fills the registration information in the + ::khui_config_node_reg structre. If the call succeeds, the + function will return KHM_ERROR_SUCCESS. + + \param[in] parent Parent of the node to be registered. Set to + NULL if the parent is the root node. + + \param[in] reg Registration information + + \param[out] new_id Receives the new unique identifier of the + configuration node. Pass in NULL if the new identifier is not + required. + + \retval KHM_ERROR_SUCCESS Success + \retval KHM_ERROR_INVALID_PARM One or more parameters, or fields + of reg were invalid + \retval KHM_ERROR_DUPLICATE A node with the same name exists as a + child of the specified parent node. + */ +KHMEXP khm_int32 KHMAPI +khui_cfg_register(khui_config_node parent, + const khui_config_node_reg * reg); + +/*!\brief Open a configuration node by name + + If successful, the \a result parameter will receive a handle to + the configuration node. Use khui_cfg_release() to release + the handle. + + \param[in] parent Parent node. Set to NULL to specify root node. + */ +KHMEXP khm_int32 KHMAPI +khui_cfg_open(khui_config_node parent, + const wchar_t * name, + khui_config_node * result); + +/*! \brief Remove a configuration node + + Marks a configuration node as deleted. Once all the handles, + including the handle specified in \a node have been released, it + will be deleted. + */ +KHMEXP khm_int32 KHMAPI +khui_cfg_remove(khui_config_node node); + +/*! \brief Hold a handle to a configuration node + + Obtains an additional hold on the handle specified by \a node. + The hold must be released with a call to \a + khui_cfg_release() + */ +KHMEXP khm_int32 KHMAPI +khui_cfg_hold(khui_config_node node); + +/*! \brief Release a handle to a configuration node + + \see khui_cfg_hold() + */ +KHMEXP khm_int32 KHMAPI +khui_cfg_release(khui_config_node node); + +/*! \brief Get a handle to the first child node + + If the call is successful, \a result will receieve a handle to the + first child node of the specified node. The returned handle must + be released with a call to khui_cfg_release() + + If \a parent does not have any child nodes, the function will + return KHM_ERROR_NOT_FOUND and set \a result to NULL. + + \param[in] parent Parent node. Set to NULL to specify root node. + \param[out] result Receives a held handle to the first child node. + + \see khui_cfg_get_next() + */ +KHMEXP khm_int32 KHMAPI +khui_cfg_get_first_child(khui_config_node parent, + khui_config_node * result); + +/*! \brief Get a handle to the first subpanel + + If the call is successful, \a result will receieve a handle to the + first subpanel node of the specified node. The returned handle + must be released with a call to khui_cfg_release() + + If \a parent does not have any subpanels, the function will return + KHM_ERROR_NOT_FOUND and set \a result to NULL. + + A subpanel node is a node which has the ::KHUI_CNFLAG_SUBPANEL + flag set. + + \param[in] parent Parent node. Set to NULL to specify root node. + \param[out] result Receives a held handle to the first subpanel node. + + \see khui_cfg_get_next() + */ +KHMEXP khm_int32 KHMAPI +khui_cfg_get_first_subpanel(khui_config_node vparent, + khui_config_node * result); + +/*! \brief Get a handle to the next sibling node + + If the call is successful, \a result will receive a held handle to + the next sibling node. The returned handle must be released with + a call to khui_cfg_release(). + + If there are no more sibling nodes, then the function return + KHM_ERROR_NOT_FOUND and set \a result to NULL. + + This function can be used to traverse a list of child nodes as + well as a list of subpanel nodes. + + */ +KHMEXP khm_int32 KHMAPI +khui_cfg_get_next(khui_config_node node, + khui_config_node * result); + +/*! \brief Get a handle to the next sibling node + + Similar to khui_cfg_get_next(), but implicitly releases the handle + that was supplied. Equivalent to doing : + + \code + khui_cfg_get_next(node, &next); + khui_cfg_release(node); + node = next; + \endcode + + \param[in,out] node On entry, specifies the node whose sibling + needs to be fetched. On exit, will have either NULL or a held + handle to the sibling node. The handle which was supplied to + the function is released. + + \retval KHM_ERROR_SUCCESS The next node is now in \a node + \retval KHM_ERROR_INVALID_PARM \a node was not a valid handle + \retval KHM_ERROR_NOT_FOUND There are no more siblings. \a node + is set to NULL. + + \note Even if there are no more siblings, the handle specified in + \a node on entry is released. + */ +KHMEXP khm_int32 KHMAPI +khui_cfg_get_next_release(khui_config_node * node); + +/*! \brief Get the name of a configuration node + + Gets the name (not the short description or the long description) + of the given configuration node. +*/ +KHMEXP khm_int32 KHMAPI +khui_cfg_get_name(khui_config_node node, + wchar_t * buf, + khm_size * cb_buf); + +/*! \brief Get registration information for a node + + The registration information that is returned is a shallow copy of + the data kept by NetIDMgr. In particular, the strings that will + be returned actually point to internal buffers and should not be + modified. + + No further action is necessary to release the information. + However, the returned data ceases to be valid when \a node is + released with a call to khui_cfg_release(). + + \param[in] node Node for which information is requested. Can be NULL if requesting information about the root node. + \param[out] reg Pointer to a ::khui_config_node_reg structure. + */ +KHMEXP khm_int32 KHMAPI +khui_cfg_get_reg(khui_config_node node, + khui_config_node_reg * reg); + +/*! \brief Internal use + + This function is used internally by NetIDMgr. Do not use. +*/ +KHMEXP HWND KHMAPI +khui_cfg_get_hwnd_inst(khui_config_node node, + khui_config_node noderef); + +/*! \brief Internal use + + This function is used internally by NetIDMgr. Do not use. +*/ +KHMEXP LPARAM KHMAPI +khui_cfg_get_param_inst(khui_config_node node, + khui_config_node noderef); + +/*! \brief Internal use + + This function is used internally by NetIDMgr. Do not use. +*/ +KHMEXP void KHMAPI +khui_cfg_set_hwnd_inst(khui_config_node node, + khui_config_node noderef, + HWND hwnd); + +/*! \brief Internal use + + This function is used internally by NetIDMgr. Do not use. +*/ +KHMEXP void KHMAPI +khui_cfg_set_param_inst(khui_config_node node, + khui_config_node noderef, + LPARAM param); + +/*! \brief Internal use + + This function is used internally by NetIDMgr. Do not use. +*/ +KHMEXP HWND KHMAPI +khui_cfg_get_hwnd(khui_config_node node); + +/*! \brief Internal use + + This function is used internally by NetIDMgr. Do not use. +*/ +KHMEXP LPARAM KHMAPI +khui_cfg_get_param(khui_config_node node); + +/*! \brief Internal use + + This function is used internally by NetIDMgr. Do not use. +*/ +KHMEXP void KHMAPI +khui_cfg_set_hwnd(khui_config_node node, HWND hwnd); + +/*! \brief Internal use + + This function is used internally by NetIDMgr. Do not use. +*/ +KHMEXP void KHMAPI +khui_cfg_set_param(khui_config_node node, LPARAM param); + +/*! \brief Internal use + + This function is used internally by NetIDMgr. Do not use. +*/ +KHMEXP void KHMAPI +khui_cfg_clear_params(void); + +/*! \brief Internal use + + This function is used internally by NetIDMgr. Do not use. +*/ +KHMEXP void KHMAPI +khui_cfg_set_configui_handle(HWND hwnd); + +/*! \brief Update the state for the specified node + + \param[in] node ::khui_config_node handle for the configuration node. + + \param[in] flags New flags. Combination of ::KHUI_CNFLAG_APPLIED and ::KHUI_CNFLAG_MODIFIED + + \param[in] mask Valid bits in \a flags + + \note Should only be called from within the dialog procedure for + the configuration node. + */ +KHMEXP void KHMAPI +khui_cfg_set_flags(khui_config_node vnode, khm_int32 flags, khm_int32 mask); + +/*! \brief Retrieve the state flags for the configuration node + + \see khui_cfg_set_flags() + */ +KHMEXP khm_int32 KHMAPI +khui_cfg_get_flags(khui_config_node vnode); + +/*! \brief Utility function: Initialize dialog box window data + + This function initializes the dialog box window data using the + ::khui_config_init_data that was passed into the WM_INITDIALOG + message. + + A new block of memory will be alocated to store the dialog data as + well as any extra space specified. A pointer to this memory block + will be stored in the \a DWLP_USER slot in the dialog box. + + The allocated block of memory must be freed by a call to + khui_cfg_free_dialog_data(). While handling other messages, the + dialog data can be retrieved using khui_cfg_get_dialog_data(). + + \param[in] hwnd_dlg Handle to the dialog box + + \param[in] data Pointer to the ::khui_config_init_data that was + passed in to WM_INITDIALOG (this is the value of \a lParam) + + \param[in] cb_extra Number of extra bytes to allocate, along with + the space required to store the contents of + ::khui_config_init_data. The extra space will be initialized + to zero. + + \param[out] new_data Receives a pointer to the copy of the + initialization data that was allocated. Optional. Pass in + NULL if this value is not required. + + \param[out] extra Receives a pointer to the block of extra memory + allocated as specified in \a cb_extra. If \a cb_extra is 0, + then this receives a NULL. + + \see khui_cfg_get_dialog_data(), khui_cfg_free_dialog_data() + */ +KHMEXP khm_int32 KHMAPI +khui_cfg_init_dialog_data(HWND hwnd_dlg, + const khui_config_init_data * data, + khm_size cb_extra, + khui_config_init_data ** new_data, + void ** extra); + +/*! \brief Utility function: Retrieves dialog data + + Retrieves the dialog data previoulsy stored using + khui_cfg_init_dialog_data(). + + \param[in] hwnd_dlg Handle to the dialog box + + \param[out] data Receives a pointer to the ::khui_config_init_data + block. + + \param[out] extra Receives a pointer to the extra memory + allocated. Optional (set to NULL if this value is not needed). +*/ +KHMEXP khm_int32 KHMAPI +khui_cfg_get_dialog_data(HWND hwnd_dlg, + khui_config_init_data ** data, + void ** extra); + +/*! \brief Utility function: Free dialog data + + Deallocates the memory allcated in a previous call to + khui_cfg_init_dialog_data() + */ +KHMEXP khm_int32 KHMAPI +khui_cfg_free_dialog_data(HWND hwnd_dlg); + +/*! \brief Sets the instance flags for a subpanel + + Since there can be more than one subpanel in a configuration + panel, they shouldn't modify the flags of the configuration node + directly. Instead, they should call this function to set the + instance flags. + + The instance flags will be merged with the flags for the + configuration node automatically. + */ +KHMEXP void KHMAPI +khui_cfg_set_flags_inst(khui_config_init_data * d, + khm_int32 flags, + khm_int32 mask); + +/*!@} */ +/*!@} */ +#endif diff --git a/src/windows/identity/uilib/khhtlink.h b/src/windows/identity/uilib/khhtlink.h new file mode 100644 index 000000000..dcbf328ff --- /dev/null +++ b/src/windows/identity/uilib/khhtlink.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KHHTLINK_H +#define __KHIMAIRA_KHHTLINK_H + +/*! \addtogroup khui +@{ */ + +/*! \defgroup khui_hyperlink Hyperlink +@{*/ + +/*! \brief A hyperlink + + When a link in a hypertext window is clicked, this structure is + passed along with the message. + + The link text fields do to point to NULL terminated strings. + Instead, the length fields should be used to extract the string. + */ +typedef struct tag_khui_htwnd_link { + RECT r; + wchar_t * id; + int id_len; + wchar_t * param; + int param_len; +} khui_htwnd_link; + +#define KHUI_MAXCCH_HTLINK_FIELD 256 +#define KHUI_MAXCB_HTLINK_FIELD (KHUI_MAXCCH_HTLINK_FIELD * sizeof(wchar_t)) + +/*!@}*/ +/*!@}*/ + +#endif diff --git a/src/windows/identity/uilib/khnewcred.h b/src/windows/identity/uilib/khnewcred.h new file mode 100644 index 000000000..1742f4a1a --- /dev/null +++ b/src/windows/identity/uilib/khnewcred.h @@ -0,0 +1,896 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KHNEWCRED_H +#define __KHIMAIRA_KHNEWCRED_H + +/******************************************************************** + New credentials windows +*********************************************************************/ + +/*! \addtogroup khui +@{ */ + +/*! \defgroup khui_cred Credentials acquisition + + Declarations associated with credentials acquisition. + +@{ */ + +/*! \brief Window message sent to credentials type panels + + This message is sent to the child windows. + + The format of the message is : + - uMsg : KHUI_WM_NC_NOTIFY + - HIWORD(wParam) : one of ::khui_wm_nc_notifications + - LPARAM : pointer to the ::khui_new_creds structure +*/ +#define KHUI_WM_NC_NOTIFY (WM_APP + 0x101) + +/*! \brief The first control ID that may be used by an identity provider */ +#define KHUI_CW_ID_MIN 8016 + +/*! \brief The maximum number of controls that may be created by an identity provider*/ +#define KHUI_CW_MAX_CTRLS 8 + +/*! \brief The maximum control ID that may be used by an identity provider */ +#define KHUI_CW_ID_MAX (KHUI_CW_ID_MIN + KHUI_CW_MAX_CTRLS - 1) + + +/*! \brief Credentials dialog notifications + + These notifications will be sent to the individual dialog + procedures of the credential type panels as a ::KHUI_WM_NC_NOTIFY + message. +*/ +enum khui_wm_nc_notifications { + WMNC_DIALOG_EXPAND = 1, + /*!< The dialog is getting expanded. + + This message is sent to the new creds dialog to set the dialog + to expanded mode. In expanded mode, all credentials type panels + are visible as opposed to the compressed mode where they are not + visible. The message is not sent to credentials type panels.*/ + + WMNC_DIALOG_SETUP, + /*!< Sent by NetIDMgr to the new creds window to notify it that + the dialog should create all the type configuration panels. + + Until this message is issued, none of the credentials type + panels exist. The credentials type panels will receive + WM_INITDIALOG etc as per the normal dialog creation process. */ + + WMNC_DIALOG_ACTIVATE, + /*!< Sent by NetIDMgr to the new creds window to notify it that + the dialog should do final initialization work and activate. */ + + WMNC_DIALOG_MOVE, + /*!< Sent by the new creds widnow to all the panels notifying them + that the NC window is moving. */ + + WMNC_DIALOG_SWITCH_PANEL, + /*!< Sent to the new creds window to cause it to switch to the + panel identified by LOWORD(wParam). + + Does nothing if the specified panel is already the current + panel. If the dialog is in compact mode and making the + specified panel visible requires switching to expanded mode, the + dialog will do so. */ + + WMNC_UPDATE_CREDTEXT, + /*!< Sent to all the credential type panels for a credentials + window to request them to update the credential text. + + When sent to the new credentials window, causes it to send the + WMNC_UPDATE_CREDTEXT message to all the credential type panels + and update the cred text window.*/ + + WMNC_CREDTEXT_LINK, + /*!< Sent to a panel dialog proc when a user clicks a credtext + embedded link that belongs to that panel */ + + WMNC_IDENTITY_CHANGE, + /*!< The primary identity has changed */ + + WMNC_CLEAR_PROMPTS, + /*!< Sent to the new creds window to clear any custom prompts */ + + WMNC_SET_PROMPTS, + /*!< Sent to the new creds window to set custom prompts */ + + WMNC_DIALOG_PREPROCESS, + /*!< Sent to all the credentials type panels to notify them that + the dialog is about to be processed */ + + WMNC_DIALOG_PROCESS, + /*!< Process the dialog and signal whether to exit the dialog or + not */ + + WMNC_DIALOG_PROCESS_COMPLETE, + /*!< Sent to the new creds window to indicate that the all the + threads have completed processing.*/ + + WMNC_TYPE_STATE, + /*!< Sent to the new creds window as notification that a + particular credentials type has changed state from enabled to + disabled or vice versa. The LPARAM member of the message + specifies the credentials type identifier for the changed + type */ + + WMNC_ADD_CONTROL_ROW, + /*!< Add a row of controls to a new cred dialog. This is an + internal message. */ +}; + +/*! \brief Notifications to the identity provider + + These notifications are sent through to the identity provider's UI + callback that was obtained using a ::KMSG_IDENT_GET_UI_CB message. + + The callback routine is called from the context of the UI thread + and is expected to not make any blocking calls. One of the + following commands will be passed in as the \a cmd parameter to + the callback. + */ +enum khui_wm_nc_ident_notify { + WMNC_IDENT_INIT, + /*!< Initialize an identity selector for a new credentials + dialog. The \a lParam parameter contains a handle to the + dialog window which will contain the identity selector + controls. The identity provider may make use of the \a + ident_aux field of the ::khui_new_creds structure to hold any + data pertaining to the credentials acquisition dialog.*/ + + WMNC_IDENT_WMSG, + /*!< Windows message. Presumably sent from one of the controls + that was created by the identity provider. The callback is + expected to return TRUE if it processed the message or FALSE + if it did not. The \a uMsg, \a wParam and \a lParam + parameters are set to the values passed in by Windows. */ + + WMNC_IDENT_EXIT, + /*!< Terminate a credentials acquisition dialog. Sent just before + the dialog is terminated. */ +}; + +/*! \name Standard credtext link IDs +@{*/ + +/*! \brief Switch the panel + + The \a id attribute of the link specifies the ordinal of the panel + to switch to. +*/ +#define CTLINKID_SWITCH_PANEL L"SwitchPanel" + +/*@}*/ + +/*forward dcl*/ +struct tag_khui_new_creds_by_type; +typedef struct tag_khui_new_creds_by_type khui_new_creds_by_type; +struct tag_khui_new_creds_prompt; +typedef struct tag_khui_new_creds_prompt khui_new_creds_prompt; +struct tag_khui_new_creds; +typedef struct tag_khui_new_creds khui_new_creds; + +typedef LRESULT +(KHMAPI *khui_ident_new_creds_cb)(khui_new_creds * nc, + UINT cmd, + HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +/*! \brief New credentials acquisition blob + + A pointer to an object of this type is passed in along with the + credentials acquisition messages. + + \see \ref cred_acq for more information +*/ +typedef struct tag_khui_new_creds { + khm_int32 magic; + + khm_int32 subtype; /*!< Subtype of the request that is + being handled through this object. + One of ::KMSG_CRED_INITIAL_CREDS, + ::KMSG_CRED_NEW_CREDS or + ::KMSG_CRED_RENEW_CREDS */ + + CRITICAL_SECTION cs; + + khm_handle *identities; /*!< The list of identities associated + with this request. The first + identity in this list (\a + identities[0]) is the primary + identity. */ + + khm_size n_identities; /*!< Number of identities in the list + \a identities */ + + khm_size nc_identities; /*!< Internal use */ + + khui_action_context ctx; /*!< An action context specifying the + context in which the credentials + acquisition operation was + launced. */ + + khm_int32 mode; /*!< The mode of the user interface. + One of ::KHUI_NC_MODE_MINI or + ::KHUI_NC_MODE_EXPANDED. */ + + HWND hwnd; /*!< Handle to the new credentials + window. */ + + struct tag_khui_new_creds_by_type **types; + /*!< Internal use */ + khm_handle *type_subs; /*!< Internal use */ + khm_size n_types; /*!< Internal use */ + khm_size nc_types; /*!< Internal use */ + + khm_int32 result; /*!< One of ::KHUI_NC_RESULT_CANCEL or + ::KHUI_NC_RESULT_GET_CREDS indicating + the result of the dialog with the + user */ + + khm_int32 response; /*!< Response. See individual message + documentation for info on what to do + with this field */ + + wchar_t *password; /*!< Not set until the dialog ends */ + + /* UI stuff */ + + wchar_t *banner; /*!< Internal use */ + wchar_t *pname; /*!< Internal use */ + khm_size n_prompts; /*!< Internal use */ + khm_size nc_prompts; /*!< Internal use */ + struct tag_khui_new_creds_prompt ** prompts; /*!< Internal use */ + + khui_ident_new_creds_cb ident_cb; /*!< Internal use */ + + wchar_t *window_title; /*!< Internal use */ + + LPARAM ident_aux; /*!< Auxilliary field which is + reserved for use by the identity + provider during the course of + conducting this dialog. */ + +} khui_new_creds; + +#define KHUI_NC_MAGIC 0x84270427 + +/*!\name Result values for khui_new_creds_t::result + @{*/ +#define KHUI_NC_RESULT_GET_CREDS 0 +#define KHUI_NC_RESULT_CANCEL 1 +/*@}*/ + +/*!\name Mode values for khui_new_creds_t::mode + @{*/ +#define KHUI_NC_MODE_MINI 0 +#define KHUI_NC_MODE_EXPANDED 1 +/*@}*/ + +/*!\name Response values for khui_new_creds_t::response + @{*/ +/*!\brief No known response */ +#define KHUI_NC_RESPONSE_NONE 0 + +/*!\brief It is okay to exit the dialog now + + This is the default, which is why it has a value of zero. In + order to prevent the dialog from exiting, set the + KHUI_NC_RESPONSE_NOEXIT response bit. */ +#define KHUI_NC_RESPONSE_EXIT 0 + +/*!\brief It is NOT okay to exit the dialog now + + Used to indicate that further user-interaction is necessary to + process the dialog. Usually this is accompanied by setting + necessary custom prompts and notifications so the user knows why + the dialog is prompting for more information. + */ +#define KHUI_NC_RESPONSE_NOEXIT 0x00000002 + +/*!\brief The dialog was processed successfully + + Since this is the default response, the value is zero. Use one of + KHUI_NC_RESPONSE_FAILED or KHUI_NC_RESPONSE_PENDING to indicate an + error or pending status. + */ +#define KHUI_NC_RESPONSE_SUCCESS 0 + +/*!\brief The processing of the dialog failed + + Self explanatory. More information about the failure should have + been reported using the khlog API, however, this response value + indicates to other credential types that depend on this credential + type that whatever it was that this credential type was supposed + to do didn't happen. +*/ +#define KHUI_NC_RESPONSE_FAILED 0x00000008 + +/*!\brief Further interaction required + + Set along with KHUI_NC_RESPONSE_NOEXIT although it is not + required. Setting this bit will automatically add the + KHUI_NC_RESPONSE_NOEXIT. + + If this bit is set, all dependent plugins will be set on hold + until another round of processing clears the pending bit. + */ +#define KHUI_NC_RESPONSE_PENDING 0x00000010 + +/*! \brief Completed + + This is automatically set if the plugin sets a response which does + not indicate either KHUI_NC_RESPONSE_NOEXIT or + KHUI_NC_RESPONSE_PENDING, which is considered to mean that the + plugin is completed processing. + + This flag cannot be explicitly specified in a response. + */ +#define KHUI_NC_RESPONSE_COMPLETED 0x00000020 + +#define KHUI_NCMASK_RESPONSE (KHUI_NC_RESPONSE_EXIT|KHUI_NC_RESPONSE_NOEXIT) +#define KHUI_NCMASK_RESULT (KHUI_NC_RESPONSE_SUCCESS|KHUI_NC_RESPONSE_FAILED|KHUI_NC_RESPONSE_PENDING) +/*@}*/ + +/*!\brief Maximum number of dependencies for a credentials type */ +#define KHUI_MAX_TYPE_DEPS 8 + +/*!\brief Maximum number of credential types for a new creds window */ +#define KHUI_MAX_NCTYPES 16 + +/*!\brief Maximum number of characters in a password + + Length includes the termininating NULL +*/ +#define KHUI_MAXCCH_PASSWORD 512 + +/*! \brief Maximum number of bytes in a password + + Includes terminating NULL +*/ +#define KHUI_MAXCB_PASSWORD (KHUI_MAXCCH_PASSWORD * sizeof(wchar_t)) + +/*! \brief Maximum number of characters in a custom banner + + Length includes terminating NULL +*/ +#define KHUI_MAXCCH_BANNER 256 + + +/*! \brief Maximum number of bytes in a custom banner + + Length includes terminating NULL +*/ +#define KHUI_MAXCB_BANNER (KHUI_MAXCCH_BANNER * sizeof(wchar_t)) + +/*! \brief Maximum number of characters in a panel name + + Length includes terminating NULL +*/ +#define KHUI_MAXCCH_PNAME 256 + +/*! \brief Maximum number of bytes in a panel name + + Length includes terminating NULL +*/ +#define KHUI_MAXCB_PNAME (KHUI_MAXCCH_PNAME * sizeof(wchar_t)) + +/*! \brief A descriptor of a panel in the new credentials acquisition tab +*/ +typedef struct tag_khui_new_creds_by_type { + khui_new_creds * nc; /*!< Internal use. Do not set */ + khm_int32 flags; /*!< Internal use. Do not set */ + + khm_int32 type; /*!< The identifier of the credentials + type */ + + khm_int32 type_deps[KHUI_MAX_TYPE_DEPS]; + /*!< credentials types that this + credential type depends on. Each + element defines a credentials type + identifier that this type depends + on for this operation. */ + + khm_size n_type_deps; /*!< Number of dependencies listed + above. Should be between 0 and + ::KHUI_MAX_TYPE_DEPS */ + + khm_size ordinal; /*!< The requested ordinal. The UI + would attempt to place this panel at + the reqested order in the list of + panels. Set to -1 if the order does + not matter. Once the dialog is + activated this field will be updated + to reflect the actual ordinal of the + panel. */ + + wchar_t *name; /*!< Name of the panel (localized, + optional). If NULL, the localized + name of the credentials type is + used. */ + + HICON icon; /*!< Icon for the panel (optional) */ + + wchar_t *tooltip; /*!< Tooltip for the panel (localized, + optional). If NULL, no tooltip will + be assigned for the panel */ + + HMODULE h_module; /*!< Handle to the module containing + the dialog resource */ + + LPWSTR dlg_template; /*!< The dialog resource */ + DLGPROC dlg_proc; /*!< The dialog procedure */ + + HWND hwnd_panel; /*!< The dialog window */ + HWND hwnd_tc; /*!< Internal use. Do not set */ + + wchar_t *credtext; /*!< A brief description of the + current state of this cred + type. (localized, optional) */ + + LPARAM aux; /*!< auxilliary field. For use by the + credential provider */ +} khui_new_creds_by_type; + +/*!\name Flags for khui_new_creds_by_type + + Note that KHUI_NC_RESPONSE_SUCCESS, KHUI_NC_RESPONSE_FAILED, + KHUI_NC_RESPONSE_PENDING are also stored in the flags. + +@{*/ +#define KHUI_NCT_FLAG_PROCESSED 1024 +#define KHUI_NCT_FLAG_DISABLED 2048 +/*@}*/ + +/*! \brief Width of a new creds dialog panel in dialog units*/ +#define NCDLG_WIDTH 300 +/*! \brief Height of a new creds dialog panel in dialog units*/ +#define NCDLG_HEIGHT 166 + +/*! \brief Width of the button bar in dialog units */ +#define NCDLG_BBAR_WIDTH 60 +/*! \brief Height of a tab button in dialog units */ +#define NCDLG_TAB_HEIGHT 15 +/*! \brief Width of a tab button in dialog units */ +#define NCDLG_TAB_WIDTH 60 + +/*! \brief A custom prompt */ +typedef struct tag_khui_new_creds_prompt { + khm_size index; /*!< Set to the zero based index + of this prompt. */ + + khm_int32 type; /*!< one of KHUI_NCPROMPT_TYPE_* */ + wchar_t * prompt; /*!< prompt string. Cannot exceed + KHUI_MAXCCH_PROMPT */ + wchar_t * def; /*!< default value. Cannot exceed + KHUI_MAXCCH_PROMPT_VALUE */ + wchar_t * value; /*!< On completion, this is set to the + value that the user entered. Will + not exceed + KHUI_MAXCCH_PROMPT_VALUE */ + + khm_int32 flags; /*!< Combination of + KHUI_NCPROMPT_FLAG_* */ + + HWND hwnd_static; /* internal use */ + HWND hwnd_edit; /* internal use */ +} khui_new_creds_prompt; + +/*! \brief The prompt input is hidden + + The input is hidden for prompts which accept passwords. The + control which represents the input will display an asterisk or a + small circle corresponding to each character typed in, but will + not show the actual character. + */ +#define KHUI_NCPROMPT_FLAG_HIDDEN 1 + +/*! \brief Internal use */ +#define KHUI_NCPROMPT_FLAG_STOCK 2 + +/*! \brief Maximum number of characters in a prompt + + Refers to the prompt text that accompanies an input control. THe + length includes the terminating NULL. + */ +#define KHUI_MAXCCH_PROMPT 256 + +/*! \brief Maximum number of bytes in a prompt + + Refers to the prompt text that accompanies an input control. THe + length includes the terminating NULL. + */ +#define KHUI_MAXCB_PROMPT (KHUI_MAXCCH_PROMPT * sizeof(wchar_t)) + +/*! \brief Maximum number of characters that can be entered in an input control + + Refers to the input control of a prompt. The length includes the + terminating NULL. + */ +#define KHUI_MAXCCH_PROMPT_VALUE 256 + +/*! \brief Maximum number of bytes that can be entered in an input control + + Refers to the input control of a prompt. The length includes the + terminating NULL. + */ +#define KHUI_MAXCB_PROMPT_VALUE (KHUI_MAXCCH_PROMPT_VALUE * sizeof(wchar_t)) + +/* from krb5.h. Redefining here because we don't want to depend on + krb5.h for all credential types */ + +/*! \brief A password control */ +#define KHUI_NCPROMPT_TYPE_PASSWORD 1 + +/*! \brief New password control + + Used when changing the password + */ +#define KHUI_NCPROMPT_TYPE_NEW_PASSWORD 2 + +/*! \brief New password again control + + Used when changing the password + */ +#define KHUI_NCPROMPT_TYPE_NEW_PASSWORD_AGAIN 3 + +/*! \brief Preauthentication (reserved) */ +#define KHUI_NCPROMPT_TYPE_PREAUTH 4 + +/*! \brief Control sizes */ +typedef enum tag_khui_control_size { + KHUI_CTRLSIZE_SMALL, + /*!< A small control fits in about 1/5 the width of the new + credentials panel */ + KHUI_CTRLSIZE_HALF, + /*!< Half size controls fit in 1/2 the width of the new + credentials panel */ + KHUI_CTRLSIZE_FULL, + /*!< Takes up the whole width of the crednetials panel */ +} khui_control_size; + +/*! \brief Internal use */ +typedef struct tag_khui_control_row { + HWND label; + HWND input; + khui_control_size size; +} khui_control_row; + +/*! \brief Create a ::khui_new_creds object + + Creates and initializes a ::khui_new_creds object. The created + object must be destroyed using the khui_cw_destroy_cred_blob() + function. + + \note Plugins should not call this function directly. The + necessary ::khui_new_creds objects will be created by + NetIDMgr. + + \see khui_cw_destroy_cred_blob() + */ +KHMEXP khm_int32 KHMAPI +khui_cw_create_cred_blob(khui_new_creds ** c); + +/*! \brief Destroy a ::khui_new_creds object + + Destroys a ::khui_new_creds object that was fomerly created using + a call to khui_cw_create_cred_blob(). + + \note Plugins should not call this function directly. The + necessary ::khui_new_creds objects will be created by + NetIDMgr. + + \see khui_cw_create_cred_blob() +*/ +KHMEXP khm_int32 KHMAPI +khui_cw_destroy_cred_blob(khui_new_creds *c); + +/*! \brief Lock the new_creds object + + When a plugin is accessing the fields of a ::khui_new_creds + object, it must first obtain a lock on the object so that other + threads will not modify the fields at the same time. Locking the + object ensures that the fields of the object will be consistent. + + Use khui_cw_unlock_nc() to undo the lock obtained through a call + to khui_cw_lock_nc(). + + It is not necessary to lock a new credentials object when + modifying it using the NetIDMgr API. + */ +KHMEXP khm_int32 KHMAPI +khui_cw_lock_nc(khui_new_creds * c); + +/*! \brief Unlock a new_creds object + + \see khui_cw_lock_nc() + */ +KHMEXP khm_int32 KHMAPI +khui_cw_unlock_nc(khui_new_creds * c); + +/*! \brief Add a new panel to a new credentials acquisition window + + See the description of ::khui_new_cred_panel for information on + how to populate it to describe a credentials type panel. + + \see khui_cw_del_type() + \see \ref cred_acq_panel_spec + \see ::khui_new_cred_panel + \see ::khui_new_creds +*/ +KHMEXP khm_int32 KHMAPI +khui_cw_add_type(khui_new_creds * c, + khui_new_creds_by_type * t); + +/*! \brief Remove a panel from a new credentials acquisition window + + \see khui_cw_add_type() + */ +KHMEXP khm_int32 KHMAPI +khui_cw_del_type(khui_new_creds * c, + khm_int32 type); + +/*! \brief Find the panel belonging to a particular credentials type + + This panel would have been added to the new credentials window + using khui_cw_add_type(). + + \see khui_cw_add_type() + */ +KHMEXP khm_int32 KHMAPI +khui_cw_find_type(khui_new_creds * c, + khm_int32 type, + khui_new_creds_by_type **t); + +/*! \brief Enable/disable a particular credentials type + + Enables or disables the panel associated with a particular + credentials type. Does not preclude the credentials type from + participating in the new credentials acquisition. However, the + user will be prevented from interacting with the specific panel. + */ +KHMEXP khm_int32 KHMAPI +khui_cw_enable_type(khui_new_creds * c, + khm_int32 type, + khm_boolean enable); + +/*! \brief Set the primary identity in a new credentials acuisition + + The primary identity dictates many of the defaults and the + semantics associated with the credentials acquision process. + Setting the primary identity also triggers the + ::WMNC_IDENTITY_CHANGE notification which will be sent to all the + credentials type panels. + + Has no effect if the primary identity is already the same as the + one specified in \a id. Specify NULL for \a id if the current + primary identity is to be cleared. + + If the primary identity is changed, then all the additional + identities associated with the new credentials acquisition dialog + will also be discarded. + */ +KHMEXP khm_int32 KHMAPI +khui_cw_set_primary_id(khui_new_creds * c, + khm_handle id); + +/*! \brief Add an additional identity to the new credentials acquisition + + Individual plugins are free to decide how to handle additional + identities. Generally, they would attempt to obtain credentials + for the primary and additional identities, but would not consider + it an error if an additional identity failed to obtain + credentials. + + Calling this function with \a id of NULL does nothing. +*/ +KHMEXP khm_int32 KHMAPI +khui_cw_add_identity(khui_new_creds * c, + khm_handle id); + +/*! \brief Clear all custom prompts + + Removes all the custom prompts from the new credentials dialog. + */ +KHMEXP khm_int32 KHMAPI +khui_cw_clear_prompts(khui_new_creds * c); + +/*! \brief Synchronize custom prompt values + + It is important to synchronize the values before accessing their + values. The controls associated with custom prompts update the + values in the ::khui_new_creds object periodically. However, the + values may lose sync intermittently. + */ +KHMEXP khm_int32 KHMAPI +khui_cw_sync_prompt_values(khui_new_creds * c); + +/*! \brief Begin custom prompting + + Begins the process of defining custom prompts. Implicity removes + all the custom prompts that are currently being displayed. The \a + banner and \a name will be displayed in separate controls above + the set of new custom prompts. + + The controls associated with the prompts will not actually be + created until all the prompts have been added using + khui_cw_add_prompt(). The number of promtps that can be added + will be exactly \a n_prompts. + */ +KHMEXP khm_int32 KHMAPI +khui_cw_begin_custom_prompts(khui_new_creds * c, + khm_size n_prompts, + wchar_t * banner, + wchar_t * name); + +/*! \brief Add a custom prompt + + After khui_cw_begin_custom_prompts() is called, the plugin should + call khui_cw_add_prompt() to add the actual prompts. The number + of prompts that can be added is the \a n_prompts value specified + in the earlier call to \a khui_cw_begin_custom_prompts(). + + Once \a n_prompts prompts have been added, the new prompts will + automatically be created and shown in the user interface. + However, if less than that prompts are added, nothing is displayed + to the user. + + \param[in] c Pointer to ::khui_new_creds structure + + \param[in] type Type of prompt. One of + ::KHUI_NCPROMPT_TYPE_PREAUTH, ::KHUI_NCPROMPT_TYPE_PASSWORD, + ::KHUI_NCPROMPT_TYPE_NEW_PASSWORD, + ::KHUI_NCPROMPT_TYPE_NEW_PASSWORD_AGAIN + + \param[in] prompt Text of the prompt. Constrained by + ::KHUI_MAXCCH_PROMPT. (Localized, required) + + \param[in] def Default value. (optional). Constrained by + ::KHUI_MAXCCH_PROMPT_VALUE. Set to NULL if not provided. + + \param[in] flags Flags. Combination of + ::KHUI_NCPROMPT_FLAG_HIDDEN + */ +KHMEXP khm_int32 KHMAPI +khui_cw_add_prompt(khui_new_creds * c, + khm_int32 type, + wchar_t * prompt, + wchar_t * def, + khm_int32 flags); + +/*! \brief Retrieve a custom prompt + + Retrieves an individual prompt. The \a idx parameter is a + zero-based index of the prompt to retrieve. The ordering is the + same as the order in which khui_cw_add_prompt() was called. + */ +KHMEXP khm_int32 KHMAPI +khui_cw_get_prompt(khui_new_creds * c, + khm_size idx, + khui_new_creds_prompt ** prompt); + +/*! \brief Get the number of custom prompts + + Retrieves the number of custom prompts currently displayed. If + this function is called between calling + khui_cw_begin_custom_prompts() and adding all the prompts, the + number returned will be the number of prompts that is expected to + be registered (i.e. the \a n_prompts parameter passed to + khui_cw_begin_custom_prompts()). + */ +KHMEXP khm_int32 KHMAPI +khui_cw_get_prompt_count(khui_new_creds * c, + khm_size * np); + + +/*! \brief Get the value of a custom prompt + + Retrieve the value of a specific prompt. The value is the string + that was typed into the input control associated with a custom + prompt. The \a idx parameter is the zero-based index of the + prompt from which to retrieve the value from. The ordering is the + same as the order in which khui_cw_add_prompt() was called. + + It is important to call khui_cw_sync_prompt_values() before + starting to call khui_cw_get_prompt_value() so that the values + returned are up-to-date. + */ +KHMEXP khm_int32 KHMAPI +khui_cw_get_prompt_value(khui_new_creds * c, + khm_size idx, + wchar_t * buf, + khm_size *cbbuf); + +/*! \brief Set the response for a plugin + + When handling ::KMSG_CRED_DIALOG_PROCESS from within the plugin + thread, it is important to set the response by calling this + function. The response can be used to signal whether the plugin + successfully obtained credentials or whether further interaction + is required, or the credentials acquisition failed. + + The response is a combination of : + - ::KHUI_NC_RESPONSE_PENDING + - ::KHUI_NC_RESPONSE_FAILED + - ::KHUI_NC_RESPONSE_PENDING + - ::KHUI_NC_RESPONSE_SUCCESS + - ::KHUI_NC_RESPONSE_NOEXIT + - ::KHUI_NC_RESPONSE_EXIT + */ +KHMEXP khm_int32 KHMAPI +khui_cw_set_response(khui_new_creds * c, + khm_int32 type, + khm_int32 response); + +/*! \brief Check whether a specified credential type panel succeeded + + This is called during the processing of KMSG_CRED_DIALOG_PROCESS + to determine whether a specified credential type succeeded in + obtaining credentials. The credential type that is being queried + should have also been listed as a dependency when adding the + current credentials type, otherwise the type queried may not have + been invoked yet. + + \return TRUE iff the queried type has reported that it successfully + completed the credentials acquision operation. + */ +KHMEXP khm_boolean KHMAPI +khui_cw_type_succeeded(khui_new_creds * c, + khm_int32 type); + +/*! \brief Add a row of controls to the identity specifier area + + Only for use by identity provider callbacks that wish to add an + identity selector control. A row of controls consist of a label + control and some input control. + + When the ::WMNC_IDENT_INIT message is sent to the identity + provider, it receives a handle to the dialog panel in the \a + lParam parameter which should be the parent window of both the + windows specified here. The control ID for any controls created + must fall within the ::KHUI_CW_ID_MIN and ::KHUI_CW_ID_MAX range. + + Both controls will be resized to fit in the row. + + If \a long_label is TRUE then the size of the label will be larger + than normal and will accomodate more text. + */ +KHMEXP khm_int32 KHMAPI +khui_cw_add_control_row(khui_new_creds * c, + HWND label, + HWND input, + khui_control_size size); + +/*!@}*/ /* Credentials acquisition */ +/*!@}*/ + +#endif diff --git a/src/windows/identity/uilib/khprops.h b/src/windows/identity/uilib/khprops.h new file mode 100644 index 000000000..b77269021 --- /dev/null +++ b/src/windows/identity/uilib/khprops.h @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KHPROPS_H +#define __KHIMAIRA_KHPROPS_H + +/********************************************************************* + Property sheets +**********************************************************************/ + +/*! \addtogroup khui + +@{*/ + +/*!\defgroup khui_pp Property sheets +@{*/ + +/* forward dcl */ +struct tag_khui_property_page; + +/*! \brief A property sheet + */ +typedef struct tag_khui_property_sheet { + PROPSHEETHEADER header; /*!< property sheet header */ + khm_int32 status; /*!< status of property sheet. One of + ::KHUI_PS_STATUS_NONE, + ::KHUI_PS_STATUS_RUNNING or + ::KHUI_PS_STATUS_DONE */ + + HWND hwnd; /*!< handle to the property sheet window. + Only valid when \a status is NOT + ::KHUI_PS_STATUS_NONE */ + + HWND hwnd_page; /*!< handle to the current page in the + property sheet. Only valid when \a + status is ::KHUI_PS_STATUS_RUNNING */ + + khui_action_context ctx; /*!< Context for the property sheet. See + documentation for + ::khui_action_context */ + + khm_handle identity; /*!< Handle to the associated identity, + if applicable */ + khm_int32 credtype; /*!< Type ID of the credentials type, if + applicable */ + khm_handle cred; /*!< Handle to the associated credential, + if applicable */ + + khm_int32 n_pages; /*!< Number of property pages. + Upperbound of ::KHUI_PS_MAX_PSP */ + + QDCL(struct tag_khui_property_page); +} khui_property_sheet; + +/*! \brief The property sheet hasn't been created yet */ +#define KHUI_PS_STATUS_NONE 0 + +/*! \brief The property sheet is visible and running */ +#define KHUI_PS_STATUS_RUNNING 1 + +/*! \brief The property sheet has completed running. + + At this point, it is safe to call khui_ps_destroy_sheet() to + destroy the property sheet. +*/ +#define KHUI_PS_STATUS_DONE 2 + +/*! \brief The property sheet is in the process of being destroyed + */ +#define KHUI_PS_STATUS_DESTROY 3 + +/*! \brief Maximum number of property sheet pages in a property sheet */ +#define KHUI_PS_MAX_PSP 16 + + +/*! \brief A property sheet page + */ +typedef struct tag_khui_property_page { + HPROPSHEETPAGE h_page; + LPPROPSHEETPAGE p_page; + HWND hwnd; + khm_int32 credtype; + khm_int32 ordinal; + + LDCL(struct tag_khui_property_page); +} khui_property_page; + +/*! \brief Special pseudo credtype for identity page + */ +#define KHUI_PPCT_IDENTITY (-8) + +/*! \brief Special pseudo credtype for credential page + */ +#define KHUI_PPCT_CREDENTIAL (-9) + +/*! \brief Create a property sheet + + \note Only called by the NetIDMgr application. + */ +KHMEXP khm_int32 KHMAPI +khui_ps_create_sheet(khui_property_sheet ** sheet); + +/*! \brief Add a page to a property sheet + + Called by a plugin or the NetIDMgr application to add a page to a + property sheet. + + Pages can only be added before the property sheet is made visible + to the user. + + \param[in] sheet The property sheet to add the page to + + \param[in] credtype The credentials type ID of the owner of the + property page. This should be set to ::KCDB_CREDTYPE_INVALID + if the type is not relevant. + + \param[in] ordinal Requested ordinal. A positive integer which is + used to order the pages in a property sheet. The pages are + ordered based on ordinal first and then alphabetically by + credentials type name. If the type is unavailable, then the + ordering is undefined. + + \param[in] ppage Pointer to structure that will be passed to + CreatePropertySheetPage() to create the property page. The + structure is not managed by NetIDMgr at all, and must exist + until the status of the property sheet changes to + ::KHUI_PS_STATUS_RUNNING. The same pointer will be found in + the \a p_page member of the ::khui_property_page structure. + + \param[out] page A pointer will be returned here that will point + to the newly created khui_property_page structure. Specify + NULL if this value is not required. You can use + khui_ps_find_page() to retrieve a pointer to the structure + later. + */ +KHMEXP khm_int32 KHMAPI +khui_ps_add_page(khui_property_sheet * sheet, + khm_int32 credtype, + khm_int32 ordinal, + LPPROPSHEETPAGE ppage, + khui_property_page ** page); + +/*! \brief Retrieve a property page structure from a property sheet + */ +KHMEXP khm_int32 KHMAPI +khui_ps_find_page(khui_property_sheet * sheet, + khm_int32 credtype, + khui_property_page ** page); + +/*! \brief Display the property sheet + + \note Only called by the NetIDMgr application + */ +KHMEXP HWND KHMAPI +khui_ps_show_sheet(HWND parent, + khui_property_sheet * sheet); + +/*! \brief Check if the given message belongs to the property sheet + + \note Only called by the NetIDMgr application + */ +KHMEXP LRESULT KHMAPI +khui_ps_check_message(khui_property_sheet * sheet, + PMSG msg); + +/*! \brief Destroy a property sheet and all associated data structures. + + \note Only called by the NetIDMgr application. +*/ +KHMEXP khm_int32 KHMAPI +khui_ps_destroy_sheet(khui_property_sheet * sheet); + +KHMEXP khm_int32 KHMAPI +khui_property_wnd_set_record(HWND hwnd_pwnd, khm_handle record); + +/*!@}*/ +/*!@}*/ + +#endif diff --git a/src/windows/identity/uilib/khremote.h b/src/windows/identity/uilib/khremote.h new file mode 100644 index 000000000..a5b9d67de --- /dev/null +++ b/src/windows/identity/uilib/khremote.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_REMOTE_H +#define __KHIMAIRA_REMOTE_H + +/*! \addtogroup khui + @{*/ +/*! \defgroup khui_remote Connecting to NetIDMgr from another process + @{*/ + +/* Leash compatibility */ +#define ID_OBTAIN_TGT_WITH_LPARAM 32809 + +#define KHUI_REQDAEMONWND_CLASS L"IDMgrRequestDaemonCls" +#define KHUI_REQDAEMONWND_NAME L"IDMgrRequestDaemon" + +#define KHUI_REQD_MAPPING_FORMAT L"Local\\NetIDMgr_DlgInfo_%lu" + +#define NETID_USERNAME_SZ 128 +#define NETID_REALM_SZ 192 +#define NETID_TITLE_SZ 256 +#define NETID_CCACHE_NAME_SZ 264 + +#define NETID_DLGTYPE_TGT 0 +#define NETID_DLGTYPE_CHPASSWD 1 +typedef struct { + DWORD size; + DWORD dlgtype; + // Tells whether dialog box is in change pwd mode or init ticket mode + struct { + WCHAR title[NETID_TITLE_SZ]; + WCHAR username[NETID_USERNAME_SZ]; + WCHAR realm[NETID_REALM_SZ]; + WCHAR ccache[NETID_CCACHE_NAME_SZ]; + DWORD use_defaults; + DWORD forwardable; + DWORD noaddresses; + DWORD lifetime; + DWORD renew_till; + DWORD proxiable; + DWORD publicip; + DWORD must_use_specified_principal; + } in; + struct { + WCHAR username[NETID_USERNAME_SZ]; + WCHAR realm[NETID_REALM_SZ]; + WCHAR ccache[NETID_CCACHE_NAME_SZ]; + } out; + // Version 1 of this structure ends here +} NETID_DLGINFO, *LPNETID_DLGINFO; + +#define NETID_DLGINFO_V1_SZ (10 * sizeof(DWORD) \ + + sizeof(WCHAR) * (NETID_TITLE_SZ + \ + 2 * NETID_USERNAME_SZ + 2 * NETID_REALM_SZ + \ + 2 * NETID_CCACHE_NAME_SZ)) + +/*!@} */ +/*!@} */ + +#endif diff --git a/src/windows/identity/uilib/khrescache.h b/src/windows/identity/uilib/khrescache.h new file mode 100644 index 000000000..96bec88ab --- /dev/null +++ b/src/windows/identity/uilib/khrescache.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_RESCACHE_H +#define __KHIMAIRA_RESCACHE_H + +#include<khdefs.h> + +KHMEXP void KHMAPI +khui_init_rescache(void); + +KHMEXP void KHMAPI +khui_exit_rescache(void); + +KHMEXP void KHMAPI +khui_cache_bitmap(UINT id, HBITMAP hbm); + +KHMEXP HBITMAP KHMAPI +khui_get_cached_bitmap(UINT id); + +typedef struct khui_ilist_t { + int cx; + int cy; + int n; + int ng; + int nused; + HBITMAP img; + HBITMAP mask; + int *idlist; +} khui_ilist; + +typedef struct khui_bitmap_t { + HBITMAP hbmp; + int cx; + int cy; +} khui_bitmap; + +KHMEXP void KHMAPI +khui_bitmap_from_hbmp(khui_bitmap * kbm, HBITMAP hbm); + +KHMEXP void KHMAPI +khui_delete_bitmap(khui_bitmap * kbm); + +KHMEXP void KHMAPI +khui_draw_bitmap(HDC hdc, int x, int y, khui_bitmap * kbm); + +/* image lists */ +KHMEXP khui_ilist * KHMAPI +khui_create_ilist(int cx, int cy, int n, int ng, int opt); + +KHMEXP BOOL KHMAPI +khui_delete_ilist(khui_ilist * il); + +KHMEXP int KHMAPI +khui_ilist_add_masked(khui_ilist * il, HBITMAP hbm, COLORREF cbkg); + +KHMEXP int KHMAPI +khui_ilist_add_masked_id(khui_ilist *il, HBITMAP hbm, + COLORREF cbkg, int id); + +KHMEXP int KHMAPI +khui_ilist_lookup_id(khui_ilist *il, int id); + +KHMEXP void KHMAPI +khui_ilist_draw(khui_ilist * il, int idx, HDC dc, int x, int y, int opt); + +KHMEXP void KHMAPI +khui_ilist_draw_bg(khui_ilist * il, int idx, HDC dc, int x, int y, + int opt, COLORREF bgcolor); + +#define khui_ilist_draw_id(il, id, dc, x, y, opt) \ + khui_ilist_draw((il),khui_ilist_lookup_id((il),(id)),(dc),(x),(y),(opt)) + +#define KHUI_SMICON_CX 16 +#define KHUI_SMICON_CY 16 + +#endif diff --git a/src/windows/identity/uilib/khtracker.h b/src/windows/identity/uilib/khtracker.h new file mode 100644 index 000000000..f03d2b425 --- /dev/null +++ b/src/windows/identity/uilib/khtracker.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_TRACKERWND_H +#define __KHIMAIRA_TRACKERWND_H + +#include<time.h> + +/*! \addtogroup khui + +@{ */ + +/*!\defgroup khui_trk Duration sliders + +The duration sliders in the UI are pseudo-log-scaled. This is based +on the assumption that people don't really need 1 minute accuracy when +setting a duration that's several hours long. As a result, it is +easier to hone in on the duration that you want without having +wizardly mouse maneuvering skillz. + +Following are the duration ranges and the granularity that is offered +in each range: + +<table> +<tr><td> Range </td><td> Increment</td></tr> +<tr><td> 0..5m </td><td> 1 min </td></tr> +<tr><td> 5m..1hr </td><td> 5 min </td></tr> +<tr><td> 1hr..4hr </td><td> 15 min </td></tr> +<tr><td> 4hr..10hr </td><td> 30 min </td></tr> +<tr><td> 10hr..24hr</td><td> 1 hr </td></tr> +<tr><td> 24hr..4d </td><td> 6 hr </td></tr> +<tr><td> 4d.. </td><td> 1 day </td></tr> +</table> + +We don't really adjust for durations over 4 days. The ranges we are +concerned with don't get much larger. + +For the purpose of writing this piece of code, I have chosen the term +"tick" to refer to a period of granularity. The number of periods of +granularity (inclusive) within a certain duration interval is referred +to as the number of ticks in the interval. For example, there are 4 +ticks between the interval of 3 minutes to 10 minutes. Each occuring +at the start of 3min, 4, 5 and 10mins. And thusly the slider control +will display 4 ticks if it is displaying the interval 3-10mins. + +@{*/ + +/*! \brief Tracker data */ +typedef struct tag_khui_tracker { + WNDPROC fn_edit; + WNDPROC fn_tracker; + HWND hw_slider; + HWND hw_edit; + int lbl_y; + int lbl_lx; + int lbl_rx; + + time_t current; /*!< Current selection */ + time_t min; /*!< Minimum (inclusive) */ + time_t max; /*!< Maximum (inclusive) */ +} khui_tracker; + +/*! \brief Install a tracker into an edit control + + Once installed, the edit control becomes a duration editor. The + tracker data structure that is supplied should remain as is for + the lifetime of the edit control. + + The tracker strucutre should have been initialized with a call to + khui_tracker_initialize() and should have valid values in the \a + min, \a max and \a current fields. + */ +KHMEXP void KHMAPI +khui_tracker_install(HWND hwnd_edit, khui_tracker * tc); + +KHMEXP void KHMAPI +khui_tracker_reposition(khui_tracker * tc); + +KHMEXP void KHMAPI +khui_tracker_initialize(khui_tracker * tc); + +KHMEXP void KHMAPI +khui_tracker_refresh(khui_tracker * tc); + +KHMEXP void KHMAPI +khui_tracker_kill_controls(khui_tracker * tc); +/*!@}*/ +/*!@}*/ + +#endif diff --git a/src/windows/identity/uilib/khuidefs.h b/src/windows/identity/uilib/khuidefs.h new file mode 100644 index 000000000..d92eb6444 --- /dev/null +++ b/src/windows/identity/uilib/khuidefs.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_KHUIDEFS_H +#define __KHIMAIRA_KHUIDEFS_H + +#include<windows.h> +#include<kmq.h> +#include<kcreddb.h> +#include<kherror.h> +#include<kherr.h> +#include<khmsgtypes.h> + +#include<khaction.h> +#include<khactiondef.h> +#include<khrescache.h> +#include<khhtlink.h> +#include<khnewcred.h> +#include<khprops.h> +#include<khalerts.h> +#include<khconfigui.h> +#include<khtracker.h> + +#include<khremote.h> + +#include<strsafe.h> + +/*! \defgroup khui User Interface + + Functions and data structures for interacting with the user + interface. + +*/ + +#endif diff --git a/src/windows/identity/uilib/propsheet.c b/src/windows/identity/uilib/propsheet.c new file mode 100644 index 000000000..749aa53bf --- /dev/null +++ b/src/windows/identity/uilib/propsheet.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khuidefs.h> +#ifdef DEBUG +#include<assert.h> +#endif + +KHMEXP khm_int32 KHMAPI khui_ps_create_sheet(khui_property_sheet ** sheet) +{ + khui_property_sheet * ps; + + ps = malloc(sizeof(*ps)); + ZeroMemory(ps, sizeof(*ps)); + + ps->header.dwSize = sizeof(ps->header); + ps->header.dwFlags = PSH_MODELESS | PSH_PROPTITLE; + ps->status = KHUI_PS_STATUS_NONE; + + *sheet = ps; + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI khui_ps_add_page( + khui_property_sheet * sheet, + khm_int32 credtype, + khm_int32 ordinal, + LPPROPSHEETPAGE ppage, + khui_property_page ** page) +{ + khui_property_page * p; + + p = malloc(sizeof(*p)); + ZeroMemory(p, sizeof(*p)); + + p->credtype = credtype; + p->ordinal = ordinal; + p->p_page = ppage; + + QPUT(sheet, p); + sheet->n_pages++; + + if(page) + *page = p; + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI khui_ps_find_page( + khui_property_sheet * sheet, + khm_int32 credtype, + khui_property_page ** page) +{ + khui_property_page * p; + + p = QTOP(sheet); + + while(p) { + if(p->credtype == credtype) + break; + p = QNEXT(p); + } + + if(p) { + *page = p; + return KHM_ERROR_SUCCESS; + } else { + *page = NULL; + return KHM_ERROR_NOT_FOUND; + } +} + +int __cdecl ps_order_func(const void *l, const void * r) { + /* l is a ** */ + return 0; +} + +KHMEXP HWND KHMAPI khui_ps_show_sheet(HWND parent, khui_property_sheet * s) +{ + khui_property_page * p; + HPROPSHEETPAGE phpsp[KHUI_PS_MAX_PSP]; + khui_property_page * ppgs[KHUI_PS_MAX_PSP]; + int i; + INT_PTR prv; + HWND hw; + + s->header.hwndParent = parent; + s->header.nPages = s->n_pages; + + p = QTOP(s); + i = 0; + while(p) { + p->h_page = CreatePropertySheetPage(p->p_page); +#ifdef DEBUG + assert(p->h_page); +#endif + ppgs[i] = p; + phpsp[i++] = p->h_page; + p = QNEXT(p); + } + + /*TODO: sort property sheets */ + + s->header.phpage = phpsp; + + prv = PropertySheet(&s->header); + if(prv <= 0) { +#ifdef DEBUG + assert(FALSE); +#endif + /*TODO: better handling for this */ + hw = NULL; + } else { + DWORD dw; + + dw = GetLastError(); + s->status = KHUI_PS_STATUS_RUNNING; + + hw = (HWND) prv; + s->hwnd = hw; + s->hwnd_page = PropSheet_GetCurrentPageHwnd(hw); + } + + return hw; +} + +KHMEXP LRESULT KHMAPI khui_ps_check_message( + khui_property_sheet * sheet, + PMSG pmsg) +{ + LRESULT lr; + + if(sheet->hwnd == NULL) + return FALSE; + + lr = PropSheet_IsDialogMessage(sheet->hwnd, pmsg); + if(lr) { + sheet->hwnd_page = PropSheet_GetCurrentPageHwnd(sheet->hwnd); + if(sheet->hwnd_page == NULL && + sheet->status == KHUI_PS_STATUS_RUNNING) + + sheet->status = KHUI_PS_STATUS_DONE; + } + + return lr; +} + +KHMEXP khm_int32 KHMAPI khui_ps_destroy_sheet(khui_property_sheet * sheet) +{ + khui_property_page * p; + + DestroyWindow(sheet->hwnd); + sheet->hwnd = NULL; + + QGET(sheet, &p); + while(p) { + free(p); + QGET(sheet, &p); + } + + free(sheet); + + return KHM_ERROR_SUCCESS; +} diff --git a/src/windows/identity/uilib/propwnd.c b/src/windows/identity/uilib/propwnd.c new file mode 100644 index 000000000..4d5d5488d --- /dev/null +++ b/src/windows/identity/uilib/propwnd.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khuidefs.h> + + +#define PW_WM_SET_RECORD WM_USER + +KHMEXP khm_int32 KHMAPI khui_property_wnd_set_record(HWND hwnd_pwnd, khm_handle record) +{ + SendMessage(hwnd_pwnd, PW_WM_SET_RECORD, 0, (LPARAM) record); + + return KHM_ERROR_SUCCESS; +} diff --git a/src/windows/identity/uilib/rescache.c b/src/windows/identity/uilib/rescache.c new file mode 100644 index 000000000..57ff30907 --- /dev/null +++ b/src/windows/identity/uilib/rescache.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khuidefs.h> +#include<hashtable.h> + +hashtable * h_bitmaps; + +khm_int32 +hash_id(const void *p) { +#pragma warning(push) +#pragma warning(disable: 4311) + return (khm_int32) p; +#pragma warning(pop) +} + +khm_int32 +comp_id(const void *p1, const void *p2) { +#pragma warning(push) +#pragma warning(disable: 4311) + return ((khm_int32)p1) - ((khm_int32)p2); +#pragma warning(pop) +} + +void +del_ref_object(const void *k, void * data) { + DeleteObject((HGDIOBJ) data); +} + +KHMEXP void KHMAPI +khui_init_rescache(void) { + h_bitmaps = hash_new_hashtable(127, hash_id, comp_id, NULL, + del_ref_object); +} + +KHMEXP void KHMAPI +khui_exit_rescache(void) { + hash_del_hashtable(h_bitmaps); +} + +KHMEXP void KHMAPI +khui_cache_bitmap(UINT id, HBITMAP hbm) { + hash_add(h_bitmaps, (void *)(size_t) id, (void *) hbm); +} + +KHMEXP HBITMAP KHMAPI +khui_get_cached_bitmap(UINT id) { + return (HBITMAP) hash_lookup(h_bitmaps, (void *)(size_t) id); +} + +KHMEXP khui_ilist * KHMAPI +khui_create_ilist(int cx, int cy, int n, int ng, int opt) { + BITMAPV5HEADER head; + HDC hdc; + + khui_ilist * il = malloc(sizeof(khui_ilist)); + il->cx = cx; + il->cy = cy; + il->n = n; + il->ng = ng; + il->nused = 0; + hdc = GetDC(NULL); + head.bV5Size = sizeof(head); + head.bV5Width = cx * n; + head.bV5Height = cy; + head.bV5Planes = 1; + head.bV5BitCount = 24; + head.bV5Compression = BI_RGB; + head.bV5SizeImage = 0; + head.bV5XPelsPerMeter = 2835; + head.bV5YPelsPerMeter = 2835; + head.bV5ClrUsed = 0; + head.bV5ClrImportant = 0; + head.bV5AlphaMask = 0; + head.bV5CSType = LCS_WINDOWS_COLOR_SPACE; + head.bV5Intent = LCS_GM_GRAPHICS; + head.bV5ProfileData = 0; + head.bV5ProfileSize = 0; + head.bV5Reserved = 0; + il->img = CreateDIBitmap(hdc, (BITMAPINFOHEADER *) &head, 0, NULL, NULL, DIB_RGB_COLORS); + il->mask = CreateBitmap(cx * n, cy, 1, 1, NULL); + il->idlist = malloc(sizeof(int) * n); + + return il; +} + +KHMEXP BOOL KHMAPI +khui_delete_ilist(khui_ilist * il) { + DeleteObject(il->img); + DeleteObject(il->mask); + free(il->idlist); + free(il); + + return TRUE; +} + +KHMEXP int KHMAPI +khui_ilist_add_masked_id(khui_ilist *il, + HBITMAP hbm, + COLORREF cbkg, + int id) { + int idx; + + idx = khui_ilist_add_masked(il,hbm,cbkg); + if(idx >= 0) { + il->idlist[idx] = id; + } + + return idx; +} + +KHMEXP int KHMAPI +khui_ilist_lookup_id(khui_ilist *il, int id) { + int i; + + for(i=0;i<il->nused;i++) { + if(il->idlist[i] == id) + return i; + } + + return -1; +} + +KHMEXP int KHMAPI +khui_ilist_add_masked(khui_ilist * il, HBITMAP hbm, COLORREF cbkg) { + HDC dcr,dci,dct,dcb; + HBITMAP hb_oldb, hb_oldi, hb_oldt; + int sx, i; + int x,y; + + dcr = GetDC(NULL); + dci = CreateCompatibleDC(dcr); + dct = CreateCompatibleDC(dcr); + dcb = CreateCompatibleDC(dcr); + ReleaseDC(NULL,dcr); + + i = il->nused++; + il->idlist[i] = -1; + sx = i * il->cx; + + hb_oldb = SelectObject(dcb, hbm); + hb_oldi = SelectObject(dci, il->img); + hb_oldt = SelectObject(dct, il->mask); + + SetBkColor(dct, RGB(0,0,0)); + SetTextColor(dct, RGB(255,255,255)); + + BitBlt(dci, sx, 0, il->cx, il->cy, dcb, 0, 0, SRCCOPY); + for(y=0;y < il->cy; y++) + for(x=0; x<il->cx; x++) { + COLORREF c = GetPixel(dcb, x, y); + if(c==cbkg) { + SetPixel(dct, sx + x, y, RGB(255,255,255)); + SetPixel(dci, sx + x, y, RGB(0,0,0)); + } else { + SetPixel(dct, sx + x, y, RGB(0,0,0)); + } + } + + SelectObject(dct, hb_oldt); + SelectObject(dci, hb_oldi); + SelectObject(dcb, hb_oldb); + + DeleteDC(dcb); + DeleteDC(dct); + DeleteDC(dci); + + return i; +} + +KHMEXP void KHMAPI +khui_ilist_draw(khui_ilist * il, + int idx, + HDC dc, + int x, + int y, + int opt) { + HDC dci; + HBITMAP hb_oldi; + + if(idx < 0) + return; + + dci = CreateCompatibleDC(dc); + + hb_oldi = SelectObject(dci, il->img); + + /*BitBlt(dc, x, y, il->cx, il->cy, dci, idx*il->cx, 0, SRCCOPY); */ + MaskBlt(dc, x, y, il->cx, il->cy, dci, idx * il->cx, 0, il->mask, idx * il->cx, 0, MAKEROP4(SRCPAINT, SRCCOPY)); +/* MaskBlt(dc, x, y, il->cx, il->cy, dci, idx * il->cx, 0, il->mask, idx * il->cx, 0, MAKEROP4(SRCINVERT, SRCCOPY)); */ + + SelectObject(dci, hb_oldi); + + DeleteDC(dci); +} + +KHMEXP void KHMAPI +khui_ilist_draw_bg(khui_ilist * il, + int idx, + HDC dc, + int x, + int y, + int opt, + COLORREF bgcolor) { + HDC dcm; + HBITMAP hb_oldm, hb_mem; + HBRUSH hbr; + RECT r; + + dcm = CreateCompatibleDC(dc); + + hb_mem = CreateCompatibleBitmap(dc, il->cx, il->cy); + + hb_oldm = SelectObject(dcm, hb_mem); + + hbr = CreateSolidBrush(bgcolor); + + r.left = 0; + r.top = 0; + r.right = il->cx; + r.bottom = il->cy; + + FillRect(dcm, &r, hbr); + + khui_ilist_draw(il,idx,dcm,0,0,opt); + + BitBlt(dc,x,y,il->cx,il->cy,dcm,0,0,SRCCOPY); + + SelectObject(dcm, hb_oldm); + + DeleteObject(hb_mem); + DeleteObject(hbr); + + DeleteDC(dcm); +} + + +KHMEXP void KHMAPI +khui_bitmap_from_hbmp(khui_bitmap * kbm, HBITMAP hbm) +{ + HDC hdc; + BITMAPINFO bmi; + + hdc = CreateCompatibleDC(NULL); + + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + + kbm->hbmp = hbm; + + if(GetDIBits(hdc, hbm, 0, 0, NULL, &bmi, DIB_RGB_COLORS)) { + kbm->cx = bmi.bmiHeader.biWidth; + kbm->cy = bmi.bmiHeader.biHeight; + } else { + kbm->cx = -1; + kbm->cy = -1; + } + + DeleteDC(hdc); +} + +KHMEXP void KHMAPI +khui_delete_bitmap(khui_bitmap * kbm) { + if (kbm->hbmp) + DeleteObject(kbm->hbmp); + kbm->hbmp = NULL; +} + +KHMEXP void KHMAPI +khui_draw_bitmap(HDC hdc, int x, int y, khui_bitmap * kbm) { + HDC hdcb = CreateCompatibleDC(hdc); + HBITMAP hbmold = SelectObject(hdcb, kbm->hbmp); + + BitBlt(hdc, x, y, kbm->cx, kbm->cy, + hdcb, 0, 0, SRCCOPY); + + SelectObject(hdcb, hbmold); + DeleteDC(hdcb); +} diff --git a/src/windows/identity/uilib/trackerwnd.c b/src/windows/identity/uilib/trackerwnd.c new file mode 100644 index 000000000..04de09641 --- /dev/null +++ b/src/windows/identity/uilib/trackerwnd.c @@ -0,0 +1,453 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khuidefs.h> +#include<commctrl.h> +#include<assert.h> + +#define K5_SLIDER_WIDTH 208 +#define K5_SLIDER_HEIGHT 40 + +#define K5_SLIDER_LBL_HPAD 5 +#define K5_SLIDER_LBL_VPAD 22 + +#define KHUI_TRACKER_PROP L"KhmTrackerData" + + +/* Count the number of ticks between tmin and tmax, inclusive +*/ +int time_t_to_ticks(time_t tmin, time_t tmax) +{ + int c = 0; + time_t lo, hi; + + tmin -= tmin % 60; /* our smallest gran is 1 min */ + if(tmax % 60) + tmax += 60 - (tmax % 60); + + lo = tmin; + +#define TFORWARD(limit,gran) \ + if(lo < limit && lo < tmax) { \ + hi = min(tmax, limit); \ + c += (int)((hi - lo) / (gran)); \ + lo = hi; \ + } + + TFORWARD(300,60); + TFORWARD(3600,300); + TFORWARD(3600*4, 60*15); + TFORWARD(3600*10,60*30); + TFORWARD(3600*24,3600); + TFORWARD(3600*24*4,3600*6); + TFORWARD(((time_t)(INFINITE & INT_MAX)),3600*24); + +#undef TFORWARD + + return c; +} + +/* Compute tmax given tmin and ticks such that there are ticks ticks + between tmin and tmax + */ +time_t ticks_to_time_t(int ticks, time_t tmin) +{ + int c = 0; + tmin -= tmin % 60; /* our smallest gran is 1 min */ + +#define SFORWARD(limit,gran) \ + if(tmin < limit && ticks > 0) { \ + c = (int) min(ticks, (limit - tmin) / (gran)); \ + tmin += c * gran; \ + ticks -= c; \ + } + + SFORWARD(300,60); + SFORWARD(3600,300); + SFORWARD(3600*4,60*15); + SFORWARD(3600*10,60*30); + SFORWARD(3600*24,3600); + SFORWARD(3600*24*4,3600*6); + SFORWARD(((time_t)(INFINITE & INT_MAX)),3600*24); + +#undef SFORWARD + + return tmin; +} + +/* Prep a tracker control which works in conjunction with the + duration edit control. + + NOTE: Runs in the context of the UI thread +*/ +void +initialize_tracker(HWND hwnd, + khui_tracker * tc) +{ + RECT r; + FILETIME ft; + wchar_t wbuf[256]; + khm_size cbbuf; + + SendMessage(tc->hw_slider, TBM_SETRANGE, 0, MAKELONG(0, time_t_to_ticks(tc->min, tc->max))); + SendMessage(tc->hw_slider, TBM_SETPOS, TRUE, (LPARAM) time_t_to_ticks(tc->min, tc->current)); + + r.left = K5_SLIDER_LBL_HPAD; + r.top = K5_SLIDER_LBL_VPAD; + r.right = K5_SLIDER_WIDTH - K5_SLIDER_LBL_HPAD; + r.bottom = r.top; + + MapDialogRect(hwnd, &r); + + tc->lbl_y = r.top; + tc->lbl_lx = r.left; + tc->lbl_rx = r.right; + + TimetToFileTimeInterval(tc->current, &ft); + cbbuf = sizeof(wbuf); + FtIntervalToString(&ft, wbuf, &cbbuf); + + SendMessage(tc->hw_edit, WM_SETTEXT, 0, (LPARAM)wbuf); +} + + +/* We instance-subclass each tracker control to provide the + functionality that we need. This is the replacement window + procedure + + NOTE: Runs in the context of the UI thread + */ +LRESULT CALLBACK +duration_tracker_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + khui_tracker * tc; + + tc = (khui_tracker *) GetProp(hwnd, KHUI_TRACKER_PROP); +#ifdef DEBUG + assert(tc != NULL); +#endif + + switch(uMsg) { + case WM_PAINT: + { + HDC hdc; + HFONT hf, hfold; + LRESULT lr; + FILETIME ft; + wchar_t buf[256]; + khm_size cbbuf; + + lr = CallWindowProc(tc->fn_tracker, hwnd, uMsg, wParam, lParam); + + /* Can't use BeginPaint here, since we already called the + window proc */ + hdc = GetWindowDC(hwnd); + + hf = (HFONT) SendMessage(tc->hw_edit, WM_GETFONT, 0, 0); + + hfold = ((HFONT) SelectObject((hdc), (HGDIOBJ)(HFONT)(hf))); + + TimetToFileTimeInterval(tc->min, &ft); + cbbuf = sizeof(buf); + FtIntervalToString(&ft, buf, &cbbuf); + + SetTextColor(hdc, RGB(0,0,0)); + SetBkMode(hdc, TRANSPARENT); + + SetTextAlign(hdc, TA_LEFT | TA_TOP | TA_NOUPDATECP); + TextOut(hdc, tc->lbl_lx, tc->lbl_y, buf, (int) wcslen(buf)); + + TimetToFileTimeInterval(tc->max, &ft); + cbbuf = sizeof(buf); + FtIntervalToString(&ft, buf, &cbbuf); + + SetTextAlign(hdc, TA_RIGHT | TA_TOP | TA_NOUPDATECP); + TextOut(hdc, tc->lbl_rx, tc->lbl_y, buf, (int) wcslen(buf)); + + ((HFONT) SelectObject((hdc), (HGDIOBJ)(HFONT)(hfold))); + + ReleaseDC(hwnd, hdc); + + return lr; + } + break; + + case WM_KILLFOCUS: + { + if((HWND)wParam != tc->hw_edit) + ShowWindow(hwnd, SW_HIDE); + } + break; + + case WM_LBUTTONUP: + case WM_MOUSEMOVE: + { + LRESULT lr; + + lr = CallWindowProc(tc->fn_tracker, hwnd, uMsg, wParam, lParam); + + if(wParam & MK_LBUTTON) { + int c = (int) SendMessage(hwnd, TBM_GETPOS, 0, 0); + time_t t = ticks_to_time_t(c, tc->min); + + if(t != tc->current) { + wchar_t buf[256]; + FILETIME ft; + khm_size cbbuf; + + tc->current = t; + //d->dirty = TRUE; + cbbuf = sizeof(buf); + TimetToFileTimeInterval(t, &ft); + FtIntervalToString(&ft, buf, &cbbuf); + SendMessage(tc->hw_edit, WM_SETTEXT, 0, (LPARAM) buf); + } + } + return lr; + } + } + + return CallWindowProc(tc->fn_tracker, hwnd, uMsg, wParam, lParam); +} + + +/* Create the subclassed duration slider on behalf of an edit control */ +void +create_edit_sliders(HWND hwnd, + HWND hwnd_dlg, + khui_tracker * tc) +{ + RECT r; + RECT rs; + + GetWindowRect(hwnd, &r); + + rs.top = 0; + rs.left = 0; + rs.right = K5_SLIDER_WIDTH; + rs.bottom = K5_SLIDER_HEIGHT; + MapDialogRect(hwnd_dlg, &rs); + rs.right -= rs.left; + rs.bottom -= rs.top; + + tc->hw_slider = + CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, + TRACKBAR_CLASS, + L"NetIDMgrTimeTickerTrackbar", + WS_POPUP | TBS_AUTOTICKS | TBS_BOTTOM | + TBS_DOWNISLEFT | TBS_HORZ | WS_CLIPCHILDREN, + r.left,r.bottom,rs.right,rs.bottom, + hwnd, + NULL, + (HINSTANCE)(DWORD_PTR) + GetWindowLongPtr(hwnd, GWLP_HINSTANCE), + NULL); + + SetProp(tc->hw_slider, KHUI_TRACKER_PROP, + (HANDLE) tc); + +#pragma warning(push) +#pragma warning(disable: 4244) + tc->fn_tracker = (WNDPROC)(LONG_PTR) SetWindowLongPtr(tc->hw_slider, GWLP_WNDPROC, (LONG_PTR) duration_tracker_proc); +#pragma warning(pop) +} + +/* An edit control is instance-subclassed to create an edit control + that holds a duration. Welcome to the window procedure. + + NOTE: Runs in the context of the UI thread + */ +LRESULT CALLBACK +duration_edit_proc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + khui_tracker * tc; + + tc = (khui_tracker *) GetProp(hwnd, KHUI_TRACKER_PROP); + +#ifdef DEBUG + assert(tc != NULL); +#endif + + switch(uMsg) { + case WM_SETFOCUS: + { + HWND p; + + p = GetParent(hwnd); + + /* we are being activated. show the panel */ + if(tc->hw_slider == NULL) { + create_edit_sliders(hwnd, p, tc); + initialize_tracker(p, tc); + } + khui_tracker_reposition(tc); + ShowWindow(tc->hw_slider, SW_SHOWNOACTIVATE); + //SetActiveWindow(p); + } + break; + + case WM_KILLFOCUS: + { + wchar_t wbuf[256]; + FILETIME ft; + khm_size cbbuf; + + if((HWND) wParam != tc->hw_slider) + ShowWindow(tc->hw_slider, SW_HIDE); + + TimetToFileTimeInterval(tc->current, &ft); + cbbuf = sizeof(wbuf); + FtIntervalToString(&ft, wbuf, &cbbuf); + + SendMessage(tc->hw_edit, WM_SETTEXT, 0, (LPARAM)wbuf); + } + break; + + case KHUI_WM_NC_NOTIFY: + if(HIWORD(wParam) == WMNC_DIALOG_SETUP) + { + HWND p; + + p = GetParent(hwnd); + + if(tc->hw_slider == NULL) { + create_edit_sliders(hwnd,p,tc); + } + + initialize_tracker(p, tc); + } + return TRUE; + + /* these messages can potentially change the text in the edit + control. We intercept them and see what changed. We may + need to grab and handle them */ + case EM_REPLACESEL: + case EM_UNDO: + case WM_UNDO: + case WM_CHAR: + case WM_UNICHAR: + { + wchar_t buf[256]; + size_t nchars; + time_t ts; + FILETIME ft; + BOOL modified; + LRESULT lr = CallWindowProc(tc->fn_edit, hwnd, uMsg, wParam, lParam); + + modified = (BOOL) SendMessage(hwnd, EM_GETMODIFY, 0, 0); + if(modified) { + /* parse the string */ + if(nchars = (size_t) SendMessage(hwnd, WM_GETTEXT, ARRAYLENGTH(buf), (LPARAM) buf)) { + buf[nchars] = 0; + + if(KHM_SUCCEEDED(IntervalStringToFt(&ft, buf))) { + ts = FtIntervalToSeconds(&ft); + if(ts >= tc->min && ts <= tc->max) { + tc->current = ts; + //d->dirty = TRUE; + if(tc->hw_slider != NULL) + SendMessage(tc->hw_slider, TBM_SETPOS, TRUE, (LPARAM) time_t_to_ticks(tc->min, tc->current)); + } + } + } + SendMessage(hwnd, EM_SETMODIFY, FALSE, 0); + } + + return lr; + } + } + + return CallWindowProc(tc->fn_edit, hwnd, uMsg, wParam, lParam); +} + +KHMEXP void KHMAPI +khui_tracker_install(HWND hwnd_edit, khui_tracker * tc) { +#ifdef DEBUG + assert(hwnd_edit); + assert(tc); +#endif + + tc->hw_edit = hwnd_edit; + + SetProp(hwnd_edit, KHUI_TRACKER_PROP, (HANDLE) tc); + +#pragma warning(push) +#pragma warning(disable: 4244) + tc->fn_edit = (WNDPROC)(LONG_PTR) + SetWindowLongPtr(hwnd_edit, GWLP_WNDPROC, + (LONG_PTR) duration_edit_proc); +#pragma warning(pop) +} + +KHMEXP void KHMAPI +khui_tracker_reposition(khui_tracker * tc) { + RECT r; + + if(tc->hw_slider && tc->hw_edit) { + GetWindowRect(tc->hw_edit, &r); + SetWindowPos(tc->hw_slider, + NULL, + r.left, r.bottom, + 0, 0, + SWP_NOOWNERZORDER | SWP_NOSIZE | + SWP_NOZORDER | SWP_NOACTIVATE); + } +} + +KHMEXP void KHMAPI +khui_tracker_initialize(khui_tracker * tc) { + ZeroMemory(tc, sizeof(*tc)); +} + +KHMEXP void KHMAPI +khui_tracker_refresh(khui_tracker * tc) { + if (!tc->hw_edit) + return; + + SendMessage(tc->hw_edit, + KHUI_WM_NC_NOTIFY, + MAKEWPARAM(0,WMNC_DIALOG_SETUP), 0); +} + +KHMEXP void KHMAPI +khui_tracker_kill_controls(khui_tracker * tc) { + if (tc->hw_slider) + DestroyWindow(tc->hw_slider); + if (tc->hw_edit) + DestroyWindow(tc->hw_edit); + tc->hw_slider = NULL; + tc->hw_edit = NULL; + tc->fn_edit = NULL; + tc->fn_tracker = NULL; +} + + diff --git a/src/windows/identity/uilib/uilibmain.c b/src/windows/identity/uilib/uilibmain.c new file mode 100644 index 000000000..65fa7aff5 --- /dev/null +++ b/src/windows/identity/uilib/uilibmain.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<khuidefs.h> + +extern void alert_init(void); +extern void alert_exit(void); + +void +uilib_process_attach(void) { + alert_init(); +} + +void +uilib_process_detach(void) { + alert_exit(); +} + diff --git a/src/windows/identity/util/Makefile b/src/windows/identity/util/Makefile new file mode 100644 index 000000000..b9fc80e20 --- /dev/null +++ b/src/windows/identity/util/Makefile @@ -0,0 +1,46 @@ +# +# Copyright (c) 2004 Massachusetts Institute of Technology +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +MODULE=util +!include <../config/Makefile.w32> + +INCFILES= \ + $(INCDIR)\utils.h \ + $(INCDIR)\hashtable.h \ + $(INCDIR)\mstring.h \ + $(INCDIR)\sync.h + +OBJFILES= \ + $(OBJ)\hashtable.obj \ + $(OBJ)\mstring.obj \ + $(OBJ)\sync.obj + +LIBFILES= + +SDKLIBFILES= + +all: mkdirs $(INCFILES) $(OBJFILES) + +clean:: + $(RM) $(INCFILES) diff --git a/src/windows/identity/util/hashtable.c b/src/windows/identity/util/hashtable.c new file mode 100644 index 000000000..41f785a95 --- /dev/null +++ b/src/windows/identity/util/hashtable.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<hashtable.h> +#include<stdlib.h> + +KHMEXP hashtable * KHMAPI hash_new_hashtable(khm_int32 n, + hash_function_t hash, + comp_function_t comp, + add_ref_function_t addr, + del_ref_function_t delr) +{ + hashtable * h; + + h = malloc(sizeof(hashtable)); + + h->n = n; + h->addr = addr; + h->comp = comp; + h->delr = delr; + h->hash = hash; + + h->bins = calloc(sizeof(hash_bin *), n); + + return h; +} + +KHMEXP void KHMAPI hash_del_hashtable(hashtable * h) { + hash_bin * b; + int i; + + for(i=0;i<h->n;i++) { + LPOP(&h->bins[i], &b); + while(b) { + if(h->delr) + h->delr(b->key, b->data); + free(b); + LPOP(&h->bins[i], &b); + } + } + + free(h); +} + +KHMEXP void KHMAPI hash_add(hashtable * h, void * key, void * data) { + int hv; + hash_bin * b; + + hv = h->hash(key) % h->n; + b = h->bins[hv]; + while(b) { + if(!h->comp(b->key, key)) { + /* found an existing value */ + if(h->delr) + h->delr(b->key, b->data); + b->key = key; + b->data = data; + if(h->addr) + h->addr(b->key, b->data); + break; + } + b = LNEXT(b); + } + + if(!b) { + b = malloc(sizeof(hash_bin)); + b->data = data; + b->key = key; + LINIT(b); + LPUSH(&h->bins[hv], b); + if(h->addr) + h->addr(b->key, b->data); + } +} + +KHMEXP void KHMAPI hash_del(hashtable * h, void * key) { + hash_bin * b; + int hv; + + hv = h->hash(key) % h->n; + + b = h->bins[hv]; + while(b) { + if(!h->comp(b->key, key)) { + /* found it */ + LDELETE(&h->bins[hv], b); + if(h->delr) + h->delr(b->key, b->data); + free(b); + break; + } + b = LNEXT(b); + } +} + +KHMEXP void * KHMAPI hash_lookup(hashtable * h, void * key) { + hash_bin * b; + int hv; + + hv = h->hash(key) % h->n; + + b = h->bins[hv]; + + while(b) { + if(!h->comp(b->key, key)) { + return b->data; + } + b = LNEXT(b); + } + + return NULL; +} + +KHMEXP khm_boolean KHMAPI hash_exist(hashtable * h, void * key) { + hash_bin * b; + int hv; + + hv = h->hash(key) % h->n; + b = h->bins[hv]; + while(b) { + if(!h->comp(b->key, key)) + return 1; + b = LNEXT(b); + } + + return 0; +} + +KHMEXP khm_int32 hash_string(const void *vs) { + /* DJB algorithm */ + + khm_int32 hv = 13331; + wchar_t * c; + + for(c = (wchar_t *) vs; *c; c++) { + hv = ((hv<<5) + hv) + (khm_int32) *c; + } + + return (hv & KHM_INT32_MAX); +} + +KHMEXP khm_int32 hash_string_comp(const void *vs1, const void *vs2) { + return wcscmp((const wchar_t *) vs1, (const wchar_t *) vs2); +} diff --git a/src/windows/identity/util/hashtable.h b/src/windows/identity/util/hashtable.h new file mode 100644 index 000000000..179d311f8 --- /dev/null +++ b/src/windows/identity/util/hashtable.h @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_HASHTABLE_H +#define __KHIMAIRA_HASHTABLE_H + +/*! \addtogroup util + @{ */ + +/*! \defgroup util_ht Hashtable + @{*/ + +#include<khdefs.h> +#include<khlist.h> + +/*! \brief A hash function + + The function should take a key as a parameter and return an + khm_int32 that serves as the hash of the key. + */ +typedef khm_int32 (*hash_function_t)(const void *key); + +/*! \brief A comparison function + + The function takes two keys and returns a value indicating the + relative ordering of the two keys. + + The return value should be: + - \b Zero if \a key1 == \a key2 + - \b Negative if \a key1 < \a key2 + - \b Positive if \a key1 > \a key2 + */ +typedef khm_int32 (*comp_function_t)(const void *key1, const void *key2); + +/*! \brief Add-reference function + + When an object is successfully added to a hashtable, this function + will be called with the \a key and \a data used to add the object. + The function is allowed to modify \a data, however, the + modification should not alter the \a key or the relationship + between \a key and \a data. + */ +typedef void (*add_ref_function_t)(const void *key, void *data); + +/*! \brief Delete-reference function + + When an object is successfully removed from the hashtable, this + function will be called. As with the add-ref function, the object + can be modified, but the \a key and the relationship between \a + key and \a data should remain intact. + + An object is removed if it is explicitly removed from the + hashtable or another object with the same \a key is added to the + hashtable. There should be a 1-1 correspondence with keys and + objects in the hashtable. The delete-reference function will be + called on all the remaining objects in the hashtable when the + hashtable is deleted. + */ +typedef void (*del_ref_function_t)(const void *key, void *data); + +typedef struct tag_hash_bin { + void * data; + void * key; + + LDCL(struct tag_hash_bin); +} hash_bin; + +typedef struct hashtable_t { + khm_int32 n; + hash_function_t hash; + comp_function_t comp; + add_ref_function_t addr; + del_ref_function_t delr; + hash_bin ** bins; +} hashtable; + +/*! \brief Create a new hashtable + + \param[in] n Number of bins in hashtable. + \param[in] hash A hash function. Required. + \param[in] comp A comparator. Required. + \param[in] addr An add-ref function. Optional; can be NULL. + \param[in] delr A del-ref function. Optional; can be NULL. + + */ +KHMEXP hashtable * KHMAPI hash_new_hashtable(khm_int32 n, + hash_function_t hash, + comp_function_t comp, + add_ref_function_t addr, + del_ref_function_t delr); + +/*! \brief Delete a hashtable + + \note Not thread-safe. Applications must serialize calls that + reference the same hashtable. + */ +KHMEXP void KHMAPI hash_del_hashtable(hashtable * h); + +/*! \brief Add an object to a hashtable + + Creates an association between the \a key and \a data in the + hashtable \a h. If there is an add-ref function defined for the + hashtable, it will be called with \a key and \data after the + object is added. If there is already an object with the same key + in the hashtable, that object will be removed (and the del-ref + function called, if appilcable) before adding the new object and + before the add-ref function is called for the new object. + + Note that two keys \a key1 and \a key2 are equal (or same) in a + hashtable if the comparator returns zero when called with \a key1 + and \a key2. + + Also note that all additions and removals to the hashtable are + done by reference. No data is copied. Any objects pointed to are + expected to exist for the duration that the object and key are + contained in the hashtable. + + \param[in] h Hashtable + \param[in] key A key. If \a key points to a location in memory, + it should be within the object pointed to by \a data, or be a + constant. Can be NULL. + \param[in] data Data. Cannot be NULL. + + \note Not thread-safe. Applications must serialize calls that + reference the same hashtable. + */ +KHMEXP void KHMAPI hash_add(hashtable * h, void * key, void * data); + +/*! \brief Delete an object from a hashtable + + Deletes the object in the hashtable \a h that is associated with + key \a key. An object is associated with key \a key if the key \a + key_o that the object is associated with is the same as \a key as + determined by the comparator. If the del-ref function is defined + for the hash-table, it will be called with the \a key_o and \a + data that was used to add the object. + + \note Not thread-safe. Applications must serialize calls that + reference the same hashtable. + */ +KHMEXP void KHMAPI hash_del(hashtable * h, void * key); + +/*! \brief Resolve and association + + Return the object that is associated with key \a key in hashtable + \a h. An object \a data is associated with key \a key in \a h if + the key \a key_o that was used to add \a data to \a h is equal to + \a key as determined by the comparator. + + Returns NULL if no association is found. + + \note Not thread-safe. Applications must serialize calls that + reference the same hashtable. + */ +KHMEXP void * KHMAPI hash_lookup(hashtable * h, void * key); + +/*! \brief Check for the presence of an association + + Returns non-zero if there exists an association between key \a key + and some object in hashtable \a h. See hash_lookup() for + definition of "association". + + Returns zero if there is no association. + + \note (hash_lookup(h,key) == NULL) iff (hash_exist(h,key)==0) + + \note Not thead-safe. Application must serialize calls that + reference the same hashtable. + */ +KHMEXP khm_boolean KHMAPI hash_exist(hashtable * h, void * key); + +/*! \brief Compute a hashvalue for a unicode string + + The hash value is computed using DJB with parameter 13331. + + This function is suitable for use as the hash function for a + hashtable if the keys are NULL terminated safe unicode strings + that are either part of the data objects or are constants. + + \param[in] str A pointer to a NULL terminated wchar_t string cast + as (void *). + */ +KHMEXP khm_int32 hash_string(const void *str); + +/*! \brief Compare two strings + + Compares two strings are returns a value that is in accordance + with the comparator for a hashtable. + + \param[in] vs1 A pointer to a NULL terminated wchar_t string cast + as (void *). + \param[in] vs2 A pointer to a NULL terminated wchar_t string cast + as (void *). + */ +KHMEXP khm_int32 hash_string_comp(const void *vs1, const void *vs2); + +/*@}*/ +/*@}*/ + +#endif diff --git a/src/windows/identity/util/mstring.c b/src/windows/identity/util/mstring.c new file mode 100644 index 000000000..e9120d600 --- /dev/null +++ b/src/windows/identity/util/mstring.c @@ -0,0 +1,516 @@ +/* +* Copyright (c) 2004 Massachusetts Institute of Technology +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, +* modify, merge, publish, distribute, sublicense, and/or sell copies +* of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +/* $Id$ */ + +#include<mstring.h> +#include<kherror.h> +#include<strsafe.h> +#include<stdlib.h> + +#define TRUE 1 +#define FALSE 0 + +KHMEXP khm_int32 KHMAPI +multi_string_init(wchar_t * ms, + khm_size cb_ms) { + if (!ms || cb_ms < sizeof(wchar_t) * 2) + return KHM_ERROR_INVALID_PARM; + + memset(ms, 0, cb_ms); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +multi_string_append( + wchar_t * ms, + khm_size * pcb_ms, + const wchar_t * str) +{ + wchar_t * s; + size_t cch_s; + size_t cch_t; + size_t cch_r; + + if(!ms || !pcb_ms || !str) + return KHM_ERROR_INVALID_PARM; + + if(FAILED(StringCchLength(str, KHM_MAXCCH_STRING, &cch_s)) || cch_s == 0) + return KHM_ERROR_INVALID_PARM; + cch_s++; + + s = ms; + + while(*s && ((s - ms) < KHM_MAXCCH_STRING)) { + if(FAILED(StringCchLength(s, KHM_MAXCB_STRING, &cch_t))) + return KHM_ERROR_INVALID_PARM; + s += cch_t + 1; + } + + if(*s || (s - ms) >= KHM_MAXCCH_STRING) { + return KHM_ERROR_INVALID_PARM; + } + + /* now s points to the second NULL of the terminating double NULL */ + + cch_r = ((s - ms) + cch_s + 1) * sizeof(wchar_t); + if(*pcb_ms < cch_r) { + *pcb_ms = cch_r; + return KHM_ERROR_TOO_LONG; + } + + *pcb_ms = cch_r; + + StringCchCopy(s, cch_s, str); + s += cch_s; + *s = 0; + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +multi_string_prepend( + wchar_t * ms, + khm_size * pcb_ms, + const wchar_t * str) +{ + size_t cch_s; + size_t cch_t; + size_t cch_r; + khm_size cb_r; + + if(!ms || !pcb_ms || !str) + return KHM_ERROR_INVALID_PARM; + + if(FAILED(StringCchLength(str, KHM_MAXCCH_STRING, &cch_s)) || cch_s == 0) + return KHM_ERROR_INVALID_PARM; + cch_s++; + + if(KHM_FAILED(multi_string_length_cch(ms, + KHM_MAXCCH_STRING, + &cch_r))) + return KHM_ERROR_INVALID_PARM; + + cch_t = cch_s + cch_r; + cb_r = cch_t * sizeof(wchar_t); + + if (*pcb_ms < cb_r) { + *pcb_ms = cb_r; + return KHM_ERROR_TOO_LONG; + } + + memmove(ms + cch_s, ms, cch_r * sizeof(wchar_t)); + memcpy(ms, str, cch_s * sizeof(wchar_t)); + + *pcb_ms = cb_r; + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +multi_string_delete( + wchar_t * ms, + const wchar_t * str, + const khm_int32 flags) +{ + wchar_t * s; + wchar_t * n; + wchar_t * e; + size_t cch; + + if(!ms || !str) + return KHM_ERROR_INVALID_PARM; + + s = multi_string_find(ms, str, flags); + if(!s) + return KHM_ERROR_NOT_FOUND; + + e = s; + n = NULL; + while(*e && (e - s) < KHM_MAXCCH_STRING) { + if(FAILED(StringCchLength(e, KHM_MAXCCH_STRING, &cch))) + return KHM_ERROR_INVALID_PARM; + e += cch + 1; + + if(!n) + n = e; + } + + if(*e || (e - s) >= KHM_MAXCCH_STRING) + return KHM_ERROR_INVALID_PARM; + + if(e == s) + return KHM_ERROR_SUCCESS; + + memmove((void *) s, (void *) n, ((e - n) + 1) * sizeof(wchar_t)); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP wchar_t * KHMAPI +multi_string_find( + const wchar_t * ms, + const wchar_t * str, + const khm_int32 flags) +{ + const wchar_t *s; + size_t cch; + size_t cch_s; + + if(!ms || !str) + return NULL; + + if(FAILED(StringCchLength(str, KHM_MAXCCH_STRING, &cch_s))) + return NULL; + + s = ms; + + while(*s && (s - ms) < KHM_MAXCCH_STRING) { + if(FAILED(StringCchLength(s, KHM_MAXCCH_STRING, &cch))) + return NULL; + /* cch++ at end */ + + if(flags & KHM_PREFIX) { + if(((flags & KHM_CASE_SENSITIVE) && !wcsncmp(s, str, cch_s)) || + (!(flags & KHM_CASE_SENSITIVE) && !wcsnicmp(s, str, cch_s))) + return (wchar_t *) s; + } else { + if((cch == cch_s) && + ((flags & KHM_CASE_SENSITIVE) && !wcsncmp(s, str, cch)) || + (!(flags & KHM_CASE_SENSITIVE) && !wcsnicmp(s, str, cch))) + return (wchar_t *) s; + } + + s += cch + 1; + } + + return NULL; +} + +KHMEXP khm_int32 KHMAPI +multi_string_to_csv( + wchar_t * csvbuf, + khm_size * pcb_csvbuf, + const wchar_t * ms) +{ + size_t cb; + size_t cbt; + const wchar_t * t; + wchar_t * d; + + if(!pcb_csvbuf || !ms) + return KHM_ERROR_INVALID_PARM; + + /* dry run */ + cbt = 0; + t = ms; + while(*t && cbt <= KHM_MAXCB_STRING) { + khm_boolean quotes = FALSE; + + if(FAILED(StringCbLength(t, KHM_MAXCB_STRING, &cb))) + return KHM_ERROR_INVALID_PARM; + cb += sizeof(wchar_t); + + cbt += cb; + + if(wcschr(t, L',')) + quotes = TRUE; + + d = (wchar_t *) t; + while(d = wcschr(d, L'"')) { + cbt += sizeof(wchar_t); /* '"'-> '""' */ + d++; + quotes = TRUE; + } + + if(quotes) + cbt += 2*sizeof(wchar_t); /* make room for quotes */ + + t += cb / sizeof(wchar_t); + } + + if(cbt > KHM_MAXCB_STRING) + return KHM_ERROR_INVALID_PARM; + + /* happens if the multi string contained no strings */ + if(cbt == 0) + cbt = sizeof(wchar_t); + + if(!csvbuf || *pcb_csvbuf < cbt) + { + *pcb_csvbuf = cbt; + return KHM_ERROR_TOO_LONG; + } + + *pcb_csvbuf = cbt; + + /* wet run */ + t = ms; + d = csvbuf; + *csvbuf = 0; + while(*t) { + const wchar_t * s; + + StringCbLength(t, KHM_MAXCB_STRING, &cb); + cb += sizeof(wchar_t); + + if(d != csvbuf) + *d++ = L','; + if(wcschr(t, L',') || wcschr(t, L'"')) { + *d++ = L'"'; + s = t; + while(*s) { + if(*s == L'"') { + *d++ = L'"'; + *d++ = L'"'; + } else + *d++ = *s; + s++; + } + *d++ = L'"'; + *d = 0; + } else { + StringCbCopy(d, cbt - ((d - csvbuf) * sizeof(wchar_t)), t); + d += cb / sizeof(wchar_t) - 1; + } + t += cb / sizeof(wchar_t); + } + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +csv_to_multi_string( + wchar_t * ms, + khm_size * pcb_ms, + const wchar_t * csv) +{ + const wchar_t * t; + wchar_t * p; + size_t cchr; + int field = 1; + + + if(!pcb_ms || !csv) + return KHM_ERROR_INVALID_PARM; + + cchr = 0; + + /* dry run */ + t = csv; + while(*t && (t - csv) < KHM_MAXCCH_STRING) { + if(field && *t == L'"') { + t++; + while(*t && (t - csv) < KHM_MAXCCH_STRING) { + if(*t == L'"') { + t++; + if(*t != L'"') + break; + } + cchr++; + t++; + } + } + + if(*t) { + cchr++; + if(*t == L',') + field = 1; + else + field = 0; + + t++; + } + } + + if((t - csv) >= KHM_MAXCCH_STRING) + return KHM_ERROR_INVALID_PARM; + + cchr++; /* last string ends */ + cchr++; /* double NULL */ + + if(!ms || *pcb_ms < (cchr * sizeof(wchar_t))) { + *pcb_ms = cchr * sizeof(wchar_t); + return KHM_ERROR_TOO_LONG; + } + + /* wet run */ + t = csv; + p = ms; + field = 1; + while(*t) { + if(field && *t == L'"') { + t++; + while(*t) { + if(*t == L'"') { + t++; + if(*t != L'"') + break; + } + *p++ = *t; + t++; + } + } + + if(*t == L',') { + *p++ = 0; + field = 1; + t++; + } else if(*t) { + *p++ = *t; + field = 0; + t++; + } + } + + *p++ = 0; /* last string ends */ + *p++ = 0; /* double NULL */ + + *pcb_ms = (p - ms) * sizeof(wchar_t); + + return KHM_ERROR_SUCCESS; +} + +KHMEXP wchar_t * KHMAPI +multi_string_next(const wchar_t * str) +{ + size_t cch; + + if(*str) { + if(FAILED(StringCchLength(str, KHM_MAXCCH_STRING, &cch))) + return NULL; + str += cch + 1; + if(*str) + return (wchar_t *) str; + else + return NULL; + } else { + return NULL; + } +} + +KHMEXP khm_size KHMAPI +multi_string_length_n(const wchar_t * str) +{ + size_t n = 0; + const wchar_t * c = str; + + while(c) { + n++; + c = multi_string_next(c); + } + + return n; +} + +KHMEXP khm_int32 KHMAPI +multi_string_length_cb(const wchar_t * str, + khm_size max_cb, + khm_size * len_cb) +{ + khm_size cch; + khm_int32 rv; + + rv = multi_string_length_cch(str, max_cb / sizeof(wchar_t), &cch); + + if(KHM_FAILED(rv)) + return rv; + + if(len_cb) + *len_cb = cch * sizeof(wchar_t); + + return rv; +} + +KHMEXP khm_int32 KHMAPI +multi_string_length_cch(const wchar_t * str, + khm_size max_cch, + khm_size * len_cch) +{ + const wchar_t * s; + khm_size cch; + size_t tcch; + + if(!str) + return KHM_ERROR_INVALID_PARM; + + s = str; + cch = 0; + while(*s && (cch < max_cch)) { + if(FAILED(StringCchLength(s, max_cch, &tcch))) + return KHM_ERROR_TOO_LONG; + cch += ++tcch; + s += tcch; + } + + if(cch >= max_cch) + return KHM_ERROR_TOO_LONG; + + if(len_cch) { + *len_cch = ++cch; + } + + return KHM_ERROR_SUCCESS; +} + +KHMEXP khm_int32 KHMAPI +multi_string_copy_cb(wchar_t * s_dest, + khm_size max_cb_dest, + const wchar_t * src) +{ + khm_size cb_dest; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(!s_dest) + return KHM_ERROR_INVALID_PARM; + + rv = multi_string_length_cb(src, max_cb_dest, &cb_dest); + if(KHM_FAILED(rv)) + return rv; + + memmove(s_dest, src, cb_dest); + + return rv; +} + +KHMEXP khm_int32 KHMAPI +multi_string_copy_cch(wchar_t * s_dest, + khm_size max_cch_dest, + const wchar_t * src) +{ + khm_size cch_dest; + khm_int32 rv = KHM_ERROR_SUCCESS; + + if(!s_dest) + return KHM_ERROR_INVALID_PARM; + + rv = multi_string_length_cch(src, max_cch_dest, &cch_dest); + if(KHM_FAILED(rv)) + return rv; + + memmove(s_dest, src, cch_dest * sizeof(wchar_t)); + + return rv; +} diff --git a/src/windows/identity/util/mstring.h b/src/windows/identity/util/mstring.h new file mode 100644 index 000000000..9b4e3800e --- /dev/null +++ b/src/windows/identity/util/mstring.h @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_MSTRING_H +#define __KHIMAIRA_MSTRING_H + +#include<khdefs.h> + +/*! \addtogroup util + @{ */ + +/*! \defgroup util_mstring Multi String and CSV functions + @{*/ + +#define KHM_PREFIX 8 + +#define KHM_CASE_SENSITIVE 16 + +#define KHM_MAXCCH_STRING 16384 + +#define KHM_MAXCB_STRING (KHM_MAXCCH_STRING * sizeof(wchar_t)) + +/*! \brief Initialize a multi-string + */ +KHMEXP khm_int32 KHMAPI +multi_string_init(wchar_t * ms, + khm_size cb_ms); + +/*! \brief Prepend a string to a multi string + + Adds the string \a str to the beginning of multi-string \a ms. + + \param[in,out] ms The multi-string to be modified. + + \param[in,out] pcb_ms A pointer to the size of the multistring. + On entry this specifies the size of the buffer pointed to by + \a ms. If the call is successful, on exit this will receive + the new size of the multi string in bytes. If the buffer is + insufficient, the function will return KHM_ERROR_TOO_LONG and + set this to the required size of the buffer in bytes. + + \param[in] str The string to prepend to \a ms. This cannot be + longer than KHM_MAXCCH_STRING in characters including the + terminating NULL. + */ +KHMEXP khm_int32 KHMAPI +multi_string_prepend(wchar_t * ms, + khm_size * pcb_ms, + const wchar_t * str); + +/*! \brief Append a string to a multi-string + + Appends the string specified by \a str to the multi string + specified by \a ms. The size of the multi string in characters + including terminating NULLs after appending \a str can not exceed + KHM_MAXCCH_STRING. + + \param[in] ms The buffer containing the multi string + + \param[in,out] pcb_ms Points to a khm_int32 indicating the size of + the buffer pointed to by \a ms. On entry this contains the + size (in bytes) of the buffer pointed to by \a ms. On exit, + contains the new size of the multi string in bytes. + + \param[in] str The string to append to the multi string. This + string cannot be NULL or an empty (zero length) string. The + length of \a str cannot exceed KHM_MAXCCH_STRING in + characters including terminating NULL. + + \retval KHM_ERROR_SUCCESS The string was appended to the multi string + + \retval KHM_ERROR_TOO_LONG The buffer pointed to by \a ms was + insufficient. The required size of the buffer is in \a pcb_ms + + \retval KHM_ERROR_INVALID_PARM One of more of the parameters were invalid. + */ +KHMEXP khm_int32 KHMAPI +multi_string_append(wchar_t * ms, + khm_size * pcb_ms, + const wchar_t * str); + +/*! \brief Deletes a string from a multi string + + Deletes the string specified by \a str from the multi string + specified by \a ms. How the string is matched to the strings in + \a ms is determined by \a flags. If more than one match is found, + then only the first match is deleted. + + \param[in] ms The multi string to modify. The length of the multi + string in characters cannot exceed KHM_MAXCCH_STRING. + + \param[in] str The string to search for + + \param[in] flags How \a str is to be matched to existing strings + in \a ms. This could be a combination of KHM_PREFIX and + KHM_CASE_SENSITIVE. If KHM_PREFIX is used, then \a ms is + searched for a string that begins with \a str. Otherwise, \a + str must match the an entire string in the multi string. If + KHM_CASE_SENSITIVE is specified, then a case sensitive match + is performed. The defualt is to use a case insensitive + search. + + \retval KHM_ERROR_SUCCESS A string was matched and deleted from \a ms + + \retval KHM_ERROR_NOT_FOUND No matches were found + + \retval KHM_ERROR_INVALID_PARM One or more parameters were incorrect. + + \note The search for the existing string is done with + multi_string_find() + */ +KHMEXP khm_int32 KHMAPI +multi_string_delete(wchar_t * ms, + const wchar_t * str, + const khm_int32 flags); + +/*! \brief Search a multi string for a string + + Searches the string specified by \a ms for a string that matches + \a str. How the match is performed is determined by \a flags. + Returns a poitner to the start of the matched string in \a ms. If + more than one string in \a ms matches \a str, then only the first + match is returned. + + \param[in] ms The multi string to search in. The length of the + multi string cannot exceed KHM_MAXCCH_STRING in characters. + + \param[in] str The string to search for + + \param[in] flags How \a str is to be matched to existing strings + in \a ms. This could be a combination of KHM_PREFIX and + KHM_CASE_SENSITIVE. If KHM_PREFIX is used, then \a ms is + searched for a string that begins with \a str. Otherwise, \a + str must match the an entire string in the multi string. If + KHM_CASE_SENSITIVE is specified, then a case sensitive match + is performed. The defualt is to use a case insensitive + search. + + \return A pointer to the start of the first matched string or + NULL if no matches were found. + + */ +KHMEXP wchar_t * KHMAPI +multi_string_find(const wchar_t * ms, + const wchar_t * str, + const khm_int32 flags); + +/*! \brief Convert a multi string to CSV + + Converts a multi string to a comma separated value string based on + the following rules. + + - Each string in the multi string is treated an individual field + + - A field is quoted if it has double quotes or commas + + - Double quotes within quoted fields are escaped by two + consecutive double quotes. + + For example: + + \code + multi_string = L"foo\0bar\0baz,quux\0ab\"cd\0"; + csv_string = L"foo,bar,\"baz,quux\",\"ab\"\"cd\""; + \endcode + + If multi_string_to_csv() is called on \a multi_string above, + you would obtain \a csv_string. + + \param[out] csvbuf The buffer to place the CSV string in. Can be + NULL if only teh size of the needed buffer is required. + + \param[in,out] pcb_csvbuf On entry, points to a khm_int32 that + holds the size of the buffer pointed to by \a csvbuf. On + exit, gets the number of bytes writted to \a csvbuf or the + required size of \a csvbuf if the buffer is too small or \a + csvbuf is NULL. + + \param[in] ms The mutli string to convert to a CSV. + + \retval KHM_ERROR_SUCCESS The multi string was successfully + converted to a CSV string. The number of bytes written is in + \a pcb_csvbuf. The count includes the terminating NULL. + + \retval KHM_ERROR_TOO_LONG The buffer was too small or \a csvbuf + was NULL. The required number of bytes in the buffer is in \a + pcb_csvbuf. + + \retval KHM_ERROR_INVALID_PARM One or more parameters were ivnalid. + + \see csv_to_multi_string() +*/ +KHMEXP khm_int32 KHMAPI +multi_string_to_csv(wchar_t * csvbuf, + khm_size * pcb_csvbuf, + const wchar_t * ms); + +/*! \brief Converts a CSV to a multi string + + Undoes what multi_string_to_csv() does. + + \param[out] ms The buffer that recieves the multi string. This + can be NULL if only the size of the buffer is requried. + + \param[in,out] pcb_ms On entry contains the number of bytes ni the + buffer poitned to by \a ms. On exit contains the number of + bytes that were copied to \a ms including terminating NULLs, + or if the buffer was too small or \a ms was NULL, holds the + size in bytes of the requied buffer. + + \param[in] csv The CSV string. + + \retval KHM_ERROR_SUCCESS The CSV string was successfully + converted. The number of bytes written is in \a pcb_ms. + + \retval KHM_ERROR_TOO_LONG The provided buffer was too small or \a + ms was NULL. The required size of the buffer in bytes is in \a + pcb_ms. + + \retval KHM_ERROR_INVALID_PARM One or more parameters were invalid. + + */ +KHMEXP khm_int32 KHMAPI +csv_to_multi_string(wchar_t * ms, + khm_size * pcb_ms, + const wchar_t * csv); + +/*! \brief Get the next string in a multi string + + When \a str is pointing to a string that is in a multi string, + this function returns a pointer to the next string in the multi + string. + + Typically, one would start by having \a str point to the start of + the multi string (which is the first string in the multi string), + and then call this function repeatedly, until it returns NULL, at + which point the end of the multi string has been reached. + + \param[in] str Pointer to a string in a multi string. Each string + in a multi string cannot exceed KHM_MAXCCH_STRING in charaters + including the terminating NULL. + + \return A pointer to the start of the next string in the multi + string or NULL if there is no more strings. + */ +KHMEXP wchar_t * KHMAPI +multi_string_next(const wchar_t * str); + +/*! \brief Get the length of a multi string in bytes + + The returned length includes the trailing double \a NULL and any + other \a NULL inbetween. + + \param[in] str Pointer to a multi string. + \param[in] max_cb Maximum size that the str can be. This can not + be larger than KHM_MAXCB_STRING. + \param[out] len_cb The length of the string in bytes if the call + is successful. + + \retval KHM_ERROR_SUCCESS The length of the string is in \a len_cb + \retval KHM_ERROR_INVALID_PARM One or more parameters were invalid + \retval KHM_ERROR_TOO_LONG The multi string is longer than \a + max_cb bytes. + */ +KHMEXP khm_int32 KHMAPI +multi_string_length_cb(const wchar_t * str, + khm_size max_cb, + khm_size * len_cb); + +/*! \brief Get the length of a multi string in characters + + The returned length includes the trailing double \a NULL and any + other \a NULL inbetween. + + \param[in] str Pointer to a multi string. + \param[in] max_cch Maximum size that the str can be. This can not + be larger than KHM_MAXCCH_STRING. + \param[out] len_cch The length of the string in characters if the call + is successful. + + \retval KHM_ERROR_SUCCESS The length of the string is in \a len_cch + \retval KHM_ERROR_INVALID_PARM One or more parameters were invalid + \retval KHM_ERROR_TOO_LONG The multi string is longer than \a + max_cch characters. + */ +KHMEXP khm_int32 KHMAPI +multi_string_length_cch(const wchar_t * str, + khm_size max_cch, + khm_size * len_cch); + +/*! \brief Get the number of strings in a multi string + */ +KHMEXP khm_size KHMAPI +multi_string_length_n(const wchar_t * str); + +/*! \brief Copy a multi string with byte counts + + Copy a multi string from one location to another. + + \param[out] s_dest Receives a copy of the multi string + \param[in] max_cb_dest Number of bytes in the buffer pointed to by + \a s_dest. + \param[in] src The source multi string + + \retval KHM_ERROR_SUCCESS The multi string was copied successfully + \retval KHM_ERROR_INVALID_PARM One or more parameters were + invalid. + \retval KHM_ERROR_TOO_LONG The size of the destination buffer was + insufficient. + */ +KHMEXP khm_int32 KHMAPI +multi_string_copy_cb(wchar_t * s_dest, + khm_size max_cb_dest, + const wchar_t * src); + +/*! \brief Copy a multi string with character count + + Copy a multi string from one location to another. + + \param[out] s_dest Receives a copy of the multi string + \param[in] max_cb_dest Number of characters in the buffer pointed + to by \a s_dest. + \param[in] src The source multi string + + \retval KHM_ERROR_SUCCESS The multi string was copied successfully + \retval KHM_ERROR_INVALID_PARM One or more parameters were + invalid. + \retval KHM_ERROR_TOO_LONG The size of the destination buffer was + insufficient. + */ +KHMEXP khm_int32 KHMAPI +multi_string_copy_cch(wchar_t * s_dest, + khm_size max_cch_dest, + const wchar_t * src); + +/*@}*/ + +#endif diff --git a/src/windows/identity/util/sync.c b/src/windows/identity/util/sync.c new file mode 100644 index 000000000..b50d484da --- /dev/null +++ b/src/windows/identity/util/sync.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#include<windows.h> +#include<sync.h> +#include<assert.h> + +#define LOCK_OPEN 0 +#define LOCK_READING 1 +#define LOCK_WRITING 2 + +KHMEXP void KHMAPI InitializeRwLock(PRWLOCK pLock) +{ + pLock->locks = 0; + pLock->status = LOCK_OPEN; + InitializeCriticalSection(&(pLock->cs)); + pLock->writewx = CreateEvent(NULL, + FALSE, /* Manual reset */ + TRUE, /* Initial state */ + NULL); + pLock->readwx = CreateEvent(NULL, + TRUE, /* Manual reset */ + TRUE, /* Initial state */ + NULL); +} + +KHMEXP void KHMAPI DeleteRwLock(PRWLOCK pLock) +{ + DeleteCriticalSection(&(pLock->cs)); + CloseHandle(pLock->readwx); + CloseHandle(pLock->writewx); +} + +KHMEXP void KHMAPI LockObtainRead(PRWLOCK pLock) +{ + while(1) { + WaitForSingleObject(pLock->readwx, INFINITE); + EnterCriticalSection(&pLock->cs); + if(pLock->status == LOCK_WRITING) { + LeaveCriticalSection(&(pLock->cs)); + continue; + } else + break; + } + pLock->locks ++; + pLock->status = LOCK_READING; + ResetEvent(pLock->writewx); + LeaveCriticalSection(&(pLock->cs)); +} + +KHMEXP void KHMAPI LockReleaseRead(PRWLOCK pLock) +{ + EnterCriticalSection(&(pLock->cs)); + assert(pLock->status == LOCK_READING); + pLock->locks--; + if(!pLock->locks) { + pLock->status = LOCK_OPEN; + SetEvent(pLock->readwx); + SetEvent(pLock->writewx); + } + LeaveCriticalSection(&(pLock->cs)); +} + +KHMEXP void KHMAPI LockObtainWrite(PRWLOCK pLock) +{ + EnterCriticalSection(&(pLock->cs)); + if(pLock->status == LOCK_WRITING && + pLock->writer == GetCurrentThreadId()) { + pLock->locks++; + LeaveCriticalSection(&(pLock->cs)); + return; + } + LeaveCriticalSection(&(pLock->cs)); + while(1) { + WaitForSingleObject(pLock->writewx, INFINITE); + EnterCriticalSection(&(pLock->cs)); + if(pLock->status == LOCK_OPEN) + break; + LeaveCriticalSection(&(pLock->cs)); + } + pLock->status = LOCK_WRITING; + pLock->locks++; + ResetEvent(pLock->readwx); + LeaveCriticalSection(&(pLock->cs)); +} + +KHMEXP void KHMAPI LockReleaseWrite(PRWLOCK pLock) +{ + EnterCriticalSection(&(pLock->cs)); + assert(pLock->status == LOCK_WRITING); + pLock->locks--; + if(!pLock->locks) { + pLock->status = LOCK_OPEN; + SetEvent(pLock->readwx); + SetEvent(pLock->writewx); + } + LeaveCriticalSection(&(pLock->cs)); +} diff --git a/src/windows/identity/util/sync.h b/src/windows/identity/util/sync.h new file mode 100644 index 000000000..5410d0e52 --- /dev/null +++ b/src/windows/identity/util/sync.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_SYNC_H +#define __KHIMAIRA_SYNC_H + +#include<khdefs.h> + +/*! \addtogroup util + @{ */ + +/*! \defgroup util_sync Synchronization + @{*/ + +/*! \brief A read/write lock + + A classic read/write lock. Allows multiple readers or a single + writer to access a protected object. Readers will wait for any + pending writer to release the lock, while a writer will wait for + any pending readers to release the lock. +*/ +typedef struct tag_rwlock { + int locks; + int status; + CRITICAL_SECTION cs; + HANDLE readwx; + HANDLE writewx; + + DWORD writer; /* TID of writer thread */ +} rw_lock_t; + +typedef rw_lock_t RWLOCK, *PRWLOCK; + +/*! \brief Initialize a read/write lock. + + A lock <b>must</b> be initialized before it can be used. + Initializing the lock does not grant the caller any locks on the + object. +*/ +KHMEXP void KHMAPI InitializeRwLock(PRWLOCK pLock); + +/*! \brief Delete a read/write lock + + Once the application is done using the read/write lock, it must be + deleted with a call to DeleteRwLock() +*/ +KHMEXP void KHMAPI DeleteRwLock(PRWLOCK pLock); + +/*! \brief Obtains a read lock on the read/write lock + + Multiple readers can obtain read locks on the same r/w lock. + However, if any thread attempts to obtain a write lock on the + object, it will wait until all readers have released the read + locks. + + Call LockReleaseRead() to release the read lock. While the same + thread may obtain multiple read locks on the same object, each + call to LockObtainRead() must have a corresponding call to + LockReleaseRead() to properly relinquish the lock. + + \see LockReleaseRead() +*/ +KHMEXP void KHMAPI LockObtainRead(PRWLOCK pLock); + +/*! \brief Relase a read lock obtained on a read/write lock + + Each call to LockObtainRead() must have a corresponding call to + LockReleaseRead(). Once all read locks are released, any threads + waiting on write locks on the object will be woken and assigned a + write lock. + + \see LockObtainRead() +*/ +KHMEXP void KHMAPI LockReleaseRead(PRWLOCK pLock); + +/*! \brief Obtains a write lock on the read/write lock + + Only a single writer is allowed to lock a single r/w lock. + However, if any thread attempts to obtain a read lock on the + object, it will wait until the writer has released the lock. + + Call LockReleaseWrite() to release the write lock. While the same + thread may obtain multiple write locks on the same object, each + call to LockObtainWrite() must have a corresponding call to + LockReleaseWrite() to properly relinquish the lock. + + \see LockReleaseWrite() +*/ +KHMEXP void KHMAPI LockObtainWrite(PRWLOCK pLock); + +/*! \brief Relase a write lock obtained on a read/write lock + + Each call to LockObtainWrite() must have a corresponding call to + LockReleaseWrite(). Once the write lock is released, any threads + waiting for read or write locks on the object will be woken and + assigned the proper lock. + + \see LockObtainWrite() +*/ +KHMEXP void KHMAPI LockReleaseWrite(PRWLOCK pLock); + +/*@}*/ +/*@}*/ + +#endif diff --git a/src/windows/identity/util/utils.h b/src/windows/identity/util/utils.h new file mode 100644 index 000000000..2e3b6b7da --- /dev/null +++ b/src/windows/identity/util/utils.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $Id$ */ + +#ifndef __KHIMAIRA_UTIL_H +#define __KHIMAIRA_UTIL_H + +/*! \defgroup util Utilities + */ +#include<hashtable.h> +#include<sync.h> +#include<mstring.h> + +#endif -- 2.26.2