summaryrefslogtreecommitdiffstats
path: root/src/windows/identity
diff options
context:
space:
mode:
authorJeffrey Altman <jaltman@secure-endpoints.com>2005-11-02 01:14:30 +0000
committerJeffrey Altman <jaltman@secure-endpoints.com>2005-11-02 01:14:30 +0000
commit5c27b523150384dd8655e739d68f01be2e4ff5d4 (patch)
tree649928f66dba979d1a84a9317b83a678e8321889 /src/windows/identity
parent5aa72f089113f917694a20144dca42049951c4da (diff)
downloadkrb5-5c27b523150384dd8655e739d68f01be2e4ff5d4.tar.gz
krb5-5c27b523150384dd8655e739d68f01be2e4ff5d4.tar.xz
krb5-5c27b523150384dd8655e739d68f01be2e4ff5d4.zip
Initial Commit Network Identity Manager for Windows
Initial commit of Network Identity Manager for KFW 3.0 Beta 1 ticket: new tags: pullup component: windows git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@17476 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/windows/identity')
-rw-r--r--src/windows/identity/Makefile179
-rw-r--r--src/windows/identity/config/Makefile133
-rw-r--r--src/windows/identity/config/Makefile.w32278
-rw-r--r--src/windows/identity/config/ccsv.pl124
-rw-r--r--src/windows/identity/config/csvschema.cfg67
-rw-r--r--src/windows/identity/doc/Makefile68
-rw-r--r--src/windows/identity/doc/cred_aquisition.h208
-rw-r--r--src/windows/identity/doc/cred_data_types.h246
-rw-r--r--src/windows/identity/doc/cred_main.h35
-rw-r--r--src/windows/identity/doc/cred_msgs.h47
-rw-r--r--src/windows/identity/doc/cred_prop_pages.h83
-rw-r--r--src/windows/identity/doc/doxyfile.cfg1000
-rw-r--r--src/windows/identity/doc/footer.html19
-rw-r--r--src/windows/identity/doc/header.html5
-rw-r--r--src/windows/identity/doc/images/Thumbs.dbbin0 -> 28160 bytes
-rw-r--r--src/windows/identity/doc/images/credview-select-outline.jpgbin0 -> 16084 bytes
-rw-r--r--src/windows/identity/doc/images/khimaira_logo.pngbin0 -> 3970 bytes
-rw-r--r--src/windows/identity/doc/images/khimaira_logo_old.jpgbin0 -> 9550 bytes
-rw-r--r--src/windows/identity/doc/images/khimaira_logo_small.pngbin0 -> 3970 bytes
-rw-r--r--src/windows/identity/doc/images/khimaira_logo_small_old.jpgbin0 -> 1665 bytes
-rw-r--r--src/windows/identity/doc/main_page.h108
-rw-r--r--src/windows/identity/doc/plugin_framework.h131
-rw-r--r--src/windows/identity/doc/plugin_locale.h107
-rw-r--r--src/windows/identity/doc/plugin_main.h126
-rw-r--r--src/windows/identity/doc/plugin_structure.h50
-rw-r--r--src/windows/identity/doc/stylesheet.css271
-rw-r--r--src/windows/identity/doc/ui_actions.h29
-rw-r--r--src/windows/identity/doc/ui_context.h187
-rw-r--r--src/windows/identity/doc/ui_main.h34
-rw-r--r--src/windows/identity/doc/ui_menus.h29
-rw-r--r--src/windows/identity/help/Index.hhk9
-rw-r--r--src/windows/identity/help/Makefile36
-rw-r--r--src/windows/identity/help/html/images/Thumbs.dbbin0 -> 3584 bytes
-rw-r--r--src/windows/identity/help/html/images/link.GIFbin0 -> 932 bytes
-rw-r--r--src/windows/identity/help/html/khm.css13
-rw-r--r--src/windows/identity/help/html/menu_exit.htm9
-rw-r--r--src/windows/identity/help/html/menu_file.htm18
-rw-r--r--src/windows/identity/help/html/menu_properties.htm9
-rw-r--r--src/windows/identity/help/html/template.htm9
-rw-r--r--src/windows/identity/help/html/welcome.htm24
-rw-r--r--src/windows/identity/help/khhelp.h23
-rw-r--r--src/windows/identity/help/netidmgr.hhp21
-rw-r--r--src/windows/identity/help/toc.hhc47
-rw-r--r--src/windows/identity/include/Makefile37
-rw-r--r--src/windows/identity/include/khdefs.h235
-rw-r--r--src/windows/identity/include/kherror.h177
-rw-r--r--src/windows/identity/include/khlist.h173
-rw-r--r--src/windows/identity/include/khmsgtypes.h700
-rw-r--r--src/windows/identity/include/khthread.h42
-rw-r--r--src/windows/identity/kconfig/Makefile51
-rw-r--r--src/windows/identity/kconfig/api.c2098
-rw-r--r--src/windows/identity/kconfig/kconfig.h823
-rw-r--r--src/windows/identity/kconfig/kconfiginternal.h114
-rw-r--r--src/windows/identity/kconfig/kconfigmain.c37
-rw-r--r--src/windows/identity/kconfig/registry.c28
-rw-r--r--src/windows/identity/kconfig/test/utiltest.c207
-rw-r--r--src/windows/identity/kcreddb/Makefile52
-rw-r--r--src/windows/identity/kcreddb/attrib.c853
-rw-r--r--src/windows/identity/kcreddb/attrib.h55
-rw-r--r--src/windows/identity/kcreddb/buf.c391
-rw-r--r--src/windows/identity/kcreddb/buf.h78
-rw-r--r--src/windows/identity/kcreddb/credential.c1047
-rw-r--r--src/windows/identity/kcreddb/credential.h70
-rw-r--r--src/windows/identity/kcreddb/credset.c1132
-rw-r--r--src/windows/identity/kcreddb/credset.h75
-rw-r--r--src/windows/identity/kcreddb/credtype.c411
-rw-r--r--src/windows/identity/kcreddb/credtype.h55
-rw-r--r--src/windows/identity/kcreddb/identity.c1537
-rw-r--r--src/windows/identity/kcreddb/identity.h62
-rw-r--r--src/windows/identity/kcreddb/init.c91
-rw-r--r--src/windows/identity/kcreddb/kcdbconfig.csv15
-rw-r--r--src/windows/identity/kcreddb/kcreddb.h3212
-rw-r--r--src/windows/identity/kcreddb/kcreddbinternal.h60
-rw-r--r--src/windows/identity/kcreddb/kcreddbmain.c40
-rw-r--r--src/windows/identity/kcreddb/lang/en_us/kcredres.rc130
-rw-r--r--src/windows/identity/kcreddb/langres.h49
-rw-r--r--src/windows/identity/kcreddb/resource.h27
-rw-r--r--src/windows/identity/kcreddb/type.c1295
-rw-r--r--src/windows/identity/kcreddb/type.h216
-rw-r--r--src/windows/identity/kherr/Makefile43
-rw-r--r--src/windows/identity/kherr/kherr.c1164
-rw-r--r--src/windows/identity/kherr/kherr.h965
-rw-r--r--src/windows/identity/kherr/kherrinternal.h67
-rw-r--r--src/windows/identity/kherr/kherrmain.c52
-rw-r--r--src/windows/identity/kmm/Makefile54
-rw-r--r--src/windows/identity/kmm/kmm.c167
-rw-r--r--src/windows/identity/kmm/kmm.h1010
-rw-r--r--src/windows/identity/kmm/kmm_module.c338
-rw-r--r--src/windows/identity/kmm/kmm_plugin.c377
-rw-r--r--src/windows/identity/kmm/kmm_reg.c291
-rw-r--r--src/windows/identity/kmm/kmm_registrar.c836
-rw-r--r--src/windows/identity/kmm/kmmconfig.csv52
-rw-r--r--src/windows/identity/kmm/kmminternal.h215
-rw-r--r--src/windows/identity/kmm/kmmmain.c157
-rw-r--r--src/windows/identity/kmm/kplugin.h146
-rw-r--r--src/windows/identity/kmm/lang/kmm_msgs.mc146
-rw-r--r--src/windows/identity/kmq/Makefile48
-rw-r--r--src/windows/identity/kmq/consumer.c423
-rw-r--r--src/windows/identity/kmq/init.c251
-rw-r--r--src/windows/identity/kmq/kmq.h743
-rw-r--r--src/windows/identity/kmq/kmqconfig.csv5
-rw-r--r--src/windows/identity/kmq/kmqinternal.h111
-rw-r--r--src/windows/identity/kmq/kmqmain.c47
-rw-r--r--src/windows/identity/kmq/msgtype.c357
-rw-r--r--src/windows/identity/kmq/publisher.c470
-rw-r--r--src/windows/identity/nidmgrdll/Makefile106
-rw-r--r--src/windows/identity/nidmgrdll/dllmain.c114
-rw-r--r--src/windows/identity/nidmgrdll/nidmgrdll.rc74
-rw-r--r--src/windows/identity/plugins/common/Makefile42
-rw-r--r--src/windows/identity/plugins/common/dynimport.c420
-rw-r--r--src/windows/identity/plugins/common/dynimport.h338
-rw-r--r--src/windows/identity/plugins/common/krb5common.c156
-rw-r--r--src/windows/identity/plugins/common/krb5common.h43
-rw-r--r--src/windows/identity/plugins/krb4/Makefile78
-rw-r--r--src/windows/identity/plugins/krb4/datarep.h37
-rw-r--r--src/windows/identity/plugins/krb4/errorfuncs.c264
-rw-r--r--src/windows/identity/plugins/krb4/errorfuncs.h80
-rw-r--r--src/windows/identity/plugins/krb4/krb4configdlg.c88
-rw-r--r--src/windows/identity/plugins/krb4/krb4funcs.c505
-rw-r--r--src/windows/identity/plugins/krb4/krb4funcs.h190
-rw-r--r--src/windows/identity/plugins/krb4/krb4plugin.c164
-rw-r--r--src/windows/identity/plugins/krb4/krbconfig.csv23
-rw-r--r--src/windows/identity/plugins/krb4/krbcred.h114
-rw-r--r--src/windows/identity/plugins/krb4/lang/en_us/langres.rc141
-rw-r--r--src/windows/identity/plugins/krb4/langres.h78
-rw-r--r--src/windows/identity/plugins/krb4/main.c191
-rw-r--r--src/windows/identity/plugins/krb5/Makefile91
-rw-r--r--src/windows/identity/plugins/krb5/datarep.c269
-rw-r--r--src/windows/identity/plugins/krb5/datarep.h37
-rw-r--r--src/windows/identity/plugins/krb5/errorfuncs.c260
-rw-r--r--src/windows/identity/plugins/krb5/errorfuncs.h75
-rw-r--r--src/windows/identity/plugins/krb5/krb5configdlg.c421
-rw-r--r--src/windows/identity/plugins/krb5/krb5funcs.c1889
-rw-r--r--src/windows/identity/plugins/krb5/krb5funcs.h121
-rw-r--r--src/windows/identity/plugins/krb5/krb5identpro.c1108
-rw-r--r--src/windows/identity/plugins/krb5/krb5newcreds.c2167
-rw-r--r--src/windows/identity/plugins/krb5/krb5plugin.c230
-rw-r--r--src/windows/identity/plugins/krb5/krb5props.c117
-rw-r--r--src/windows/identity/plugins/krb5/krb5util.c1362
-rw-r--r--src/windows/identity/plugins/krb5/krbconfig.csv34
-rw-r--r--src/windows/identity/plugins/krb5/krbcred.h182
-rw-r--r--src/windows/identity/plugins/krb5/lang/en_us/langres.rc406
-rw-r--r--src/windows/identity/plugins/krb5/lang/krb5_msgs.mc151
-rw-r--r--src/windows/identity/plugins/krb5/langres.h127
-rw-r--r--src/windows/identity/plugins/krb5/main.c387
-rw-r--r--src/windows/identity/ui/Makefile81
-rw-r--r--src/windows/identity/ui/aboutwnd.c147
-rw-r--r--src/windows/identity/ui/aboutwnd.h33
-rw-r--r--src/windows/identity/ui/appglobal.h74
-rw-r--r--src/windows/identity/ui/cfg_general_wnd.c227
-rw-r--r--src/windows/identity/ui/cfg_identities_wnd.c1256
-rw-r--r--src/windows/identity/ui/cfg_notif_wnd.c313
-rw-r--r--src/windows/identity/ui/cfg_plugins_wnd.c318
-rw-r--r--src/windows/identity/ui/configwnd.c839
-rw-r--r--src/windows/identity/ui/configwnd.h80
-rw-r--r--src/windows/identity/ui/credfuncs.c827
-rw-r--r--src/windows/identity/ui/credfuncs.h72
-rw-r--r--src/windows/identity/ui/credwnd.c3223
-rw-r--r--src/windows/identity/ui/credwnd.h237
-rw-r--r--src/windows/identity/ui/htmlwnd.h0
-rw-r--r--src/windows/identity/ui/htwnd.c1070
-rw-r--r--src/windows/identity/ui/htwnd.h55
-rw-r--r--src/windows/identity/ui/images/Thumbs.dbbin0 -> 116224 bytes
-rw-r--r--src/windows/identity/ui/images/app_notify_error.icobin0 -> 25214 bytes
-rw-r--r--src/windows/identity/ui/images/app_notify_info.icobin0 -> 25214 bytes
-rw-r--r--src/windows/identity/ui/images/app_notify_none.icobin0 -> 25214 bytes
-rw-r--r--src/windows/identity/ui/images/app_notify_warn.icobin0 -> 25214 bytes
-rw-r--r--src/windows/identity/ui/images/bitmap1.bmpbin0 -> 1270 bytes
-rw-r--r--src/windows/identity/ui/images/cfg_applied.icobin0 -> 1406 bytes
-rw-r--r--src/windows/identity/ui/images/cfg_default.icobin0 -> 1406 bytes
-rw-r--r--src/windows/identity/ui/images/cfg_deleted.icobin0 -> 1406 bytes
-rw-r--r--src/windows/identity/ui/images/cfg_mod.icobin0 -> 1406 bytes
-rw-r--r--src/windows/identity/ui/images/chpw-dis-sm.bmpbin0 -> 822 bytes
-rw-r--r--src/windows/identity/ui/images/chpw-dis.bmpbin0 -> 2430 bytes
-rw-r--r--src/windows/identity/ui/images/chpw-sm.bmpbin0 -> 822 bytes
-rw-r--r--src/windows/identity/ui/images/chpw.bmpbin0 -> 2430 bytes
-rw-r--r--src/windows/identity/ui/images/disabled.icobin0 -> 2166 bytes
-rw-r--r--src/windows/identity/ui/images/enabled.icobin0 -> 2166 bytes
-rw-r--r--src/windows/identity/ui/images/flag-critical.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/flag-warning.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/flag_expired.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/help-sm.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/help.bmpbin0 -> 2430 bytes
-rw-r--r--src/windows/identity/ui/images/icon1.icobin0 -> 766 bytes
-rw-r--r--src/windows/identity/ui/images/id-delete-dis-sm.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/id-delete-dis.bmpbin0 -> 3186 bytes
-rw-r--r--src/windows/identity/ui/images/id-delete-sm.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/id-delete.bmpbin0 -> 3186 bytes
-rw-r--r--src/windows/identity/ui/images/id-dis-sm.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/id-dis.bmpbin0 -> 3186 bytes
-rw-r--r--src/windows/identity/ui/images/id-new-dis-sm.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/id-new-dis.bmpbin0 -> 3186 bytes
-rw-r--r--src/windows/identity/ui/images/id-new-sm.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/id-new.bmpbin0 -> 3186 bytes
-rw-r--r--src/windows/identity/ui/images/id-refresh-dis.bmpbin0 -> 3186 bytes
-rw-r--r--src/windows/identity/ui/images/id-refresh-sm-dis.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/id-refresh-sm.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/id-refresh.bmpbin0 -> 3186 bytes
-rw-r--r--src/windows/identity/ui/images/id-sm.bmpbin0 -> 822 bytes
-rw-r--r--src/windows/identity/ui/images/id.bmpbin0 -> 3186 bytes
-rw-r--r--src/windows/identity/ui/images/id.icobin0 -> 2166 bytes
-rw-r--r--src/windows/identity/ui/images/ident.pngbin0 -> 423 bytes
-rw-r--r--src/windows/identity/ui/images/import-dis.bmpbin0 -> 2430 bytes
-rw-r--r--src/windows/identity/ui/images/import-sm-dis.bmpbin0 -> 774 bytes
-rw-r--r--src/windows/identity/ui/images/import-sm.bmpbin0 -> 774 bytes
-rw-r--r--src/windows/identity/ui/images/import.bmpbin0 -> 2430 bytes
-rw-r--r--src/windows/identity/ui/images/khimaira-cfg.bmpbin0 -> 30056 bytes
-rw-r--r--src/windows/identity/ui/images/logo_shade.bmpbin0 -> 30056 bytes
-rw-r--r--src/windows/identity/ui/images/main_app.icobin0 -> 25214 bytes
-rw-r--r--src/windows/identity/ui/images/main_app_old.icobin0 -> 7854 bytes
-rw-r--r--src/windows/identity/ui/images/tb-blank-small.bmpbin0 -> 774 bytes
-rw-r--r--src/windows/identity/ui/images/tb-blank.bmpbin0 -> 2430 bytes
-rw-r--r--src/windows/identity/ui/images/tb-space.bmpbin0 -> 2430 bytes
-rw-r--r--src/windows/identity/ui/images/text1138.pngbin0 -> 378 bytes
-rw-r--r--src/windows/identity/ui/images/tk-delete-dis-sm.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/tk-delete-dis.bmpbin0 -> 3186 bytes
-rw-r--r--src/windows/identity/ui/images/tk-delete-sm.bmpbin0 -> 822 bytes
-rw-r--r--src/windows/identity/ui/images/tk-delete.bmpbin0 -> 2430 bytes
-rw-r--r--src/windows/identity/ui/images/tk-dis-sm.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/tk-dis.bmpbin0 -> 3186 bytes
-rw-r--r--src/windows/identity/ui/images/tk-new-dis-sm.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/tk-new-dis.bmpbin0 -> 3186 bytes
-rw-r--r--src/windows/identity/ui/images/tk-new-sm.bmpbin0 -> 822 bytes
-rw-r--r--src/windows/identity/ui/images/tk-new.bmpbin0 -> 2430 bytes
-rw-r--r--src/windows/identity/ui/images/tk-refresh-dis-sm.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/tk-refresh-dis.bmpbin0 -> 3186 bytes
-rw-r--r--src/windows/identity/ui/images/tk-refresh-sm.bmpbin0 -> 822 bytes
-rw-r--r--src/windows/identity/ui/images/tk-refresh.bmpbin0 -> 2430 bytes
-rw-r--r--src/windows/identity/ui/images/tk-sm.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/tk.bmpbin0 -> 3186 bytes
-rw-r--r--src/windows/identity/ui/images/vw-refresh-sm.bmpbin0 -> 822 bytes
-rw-r--r--src/windows/identity/ui/images/vw-refresh.bmpbin0 -> 2430 bytes
-rw-r--r--src/windows/identity/ui/images/wdg_collapsed.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/wdg_collapsed_hi.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/wdg_credtype.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/wdg_expanded.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/wdg_expanded_hi.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/wdg_flag.bmpbin0 -> 1014 bytes
-rw-r--r--src/windows/identity/ui/images/wgt_arrow_collapse.icobin0 -> 1406 bytes
-rw-r--r--src/windows/identity/ui/images/wgt_arrow_expand.icobin0 -> 1406 bytes
-rw-r--r--src/windows/identity/ui/khmapp.h69
-rw-r--r--src/windows/identity/ui/lang/en_us/khapp.rc728
-rw-r--r--src/windows/identity/ui/main.c442
-rw-r--r--src/windows/identity/ui/mainmenu.c566
-rw-r--r--src/windows/identity/ui/mainmenu.h58
-rw-r--r--src/windows/identity/ui/mainwnd.c679
-rw-r--r--src/windows/identity/ui/mainwnd.h60
-rw-r--r--src/windows/identity/ui/makeacceldef.pl29
-rw-r--r--src/windows/identity/ui/makeactiondef.pl29
-rw-r--r--src/windows/identity/ui/netidmgr.exe.manifest.i38622
-rw-r--r--src/windows/identity/ui/netidmgr.manifest.i386.vc722
-rw-r--r--src/windows/identity/ui/netidmgr.manifest.i386.vc7.debug22
-rw-r--r--src/windows/identity/ui/netidmgr.manifest.i386.vc831
-rw-r--r--src/windows/identity/ui/netidmgr.manifest.i386.vc8.debug31
-rw-r--r--src/windows/identity/ui/newcredwnd.c1694
-rw-r--r--src/windows/identity/ui/newcredwnd.h101
-rw-r--r--src/windows/identity/ui/notifier.c1079
-rw-r--r--src/windows/identity/ui/notifier.h45
-rw-r--r--src/windows/identity/ui/passwnd.c133
-rw-r--r--src/windows/identity/ui/passwnd.h39
-rw-r--r--src/windows/identity/ui/propertywnd.c248
-rw-r--r--src/windows/identity/ui/propertywnd.h36
-rw-r--r--src/windows/identity/ui/reqdaemon.c396
-rw-r--r--src/windows/identity/ui/reqdaemon.h42
-rw-r--r--src/windows/identity/ui/resource.h313
-rw-r--r--src/windows/identity/ui/statusbar.c149
-rw-r--r--src/windows/identity/ui/statusbar.h53
-rw-r--r--src/windows/identity/ui/timer.c637
-rw-r--r--src/windows/identity/ui/timer.h100
-rw-r--r--src/windows/identity/ui/toolbar.c366
-rw-r--r--src/windows/identity/ui/toolbar.h52
-rw-r--r--src/windows/identity/ui/uiconfig.csv111
-rw-r--r--src/windows/identity/uilib/Makefile61
-rw-r--r--src/windows/identity/uilib/accel.csv17
-rw-r--r--src/windows/identity/uilib/acceldef.cfg50
-rw-r--r--src/windows/identity/uilib/action.c1019
-rw-r--r--src/windows/identity/uilib/actiondef.cfg64
-rw-r--r--src/windows/identity/uilib/actions.csv37
-rw-r--r--src/windows/identity/uilib/alert.c350
-rw-r--r--src/windows/identity/uilib/configui.c1001
-rw-r--r--src/windows/identity/uilib/configui.h74
-rw-r--r--src/windows/identity/uilib/creddlg.c671
-rw-r--r--src/windows/identity/uilib/khaction.h566
-rw-r--r--src/windows/identity/uilib/khactiondef.h134
-rw-r--r--src/windows/identity/uilib/khalerts.h372
-rw-r--r--src/windows/identity/uilib/khconfigui.h588
-rw-r--r--src/windows/identity/uilib/khhtlink.h58
-rw-r--r--src/windows/identity/uilib/khnewcred.h896
-rw-r--r--src/windows/identity/uilib/khprops.h202
-rw-r--r--src/windows/identity/uilib/khremote.h84
-rw-r--r--src/windows/identity/uilib/khrescache.h100
-rw-r--r--src/windows/identity/uilib/khtracker.h113
-rw-r--r--src/windows/identity/uilib/khuidefs.h58
-rw-r--r--src/windows/identity/uilib/propsheet.c188
-rw-r--r--src/windows/identity/uilib/propwnd.c37
-rw-r--r--src/windows/identity/uilib/rescache.c301
-rw-r--r--src/windows/identity/uilib/trackerwnd.c453
-rw-r--r--src/windows/identity/uilib/uilibmain.c41
-rw-r--r--src/windows/identity/util/Makefile46
-rw-r--r--src/windows/identity/util/hashtable.c167
-rw-r--r--src/windows/identity/util/hashtable.h223
-rw-r--r--src/windows/identity/util/mstring.c516
-rw-r--r--src/windows/identity/util/mstring.h361
-rw-r--r--src/windows/identity/util/sync.c121
-rw-r--r--src/windows/identity/util/sync.h128
-rw-r--r--src/windows/identity/util/utils.h36
306 files changed, 67039 insertions, 0 deletions
diff --git a/src/windows/identity/Makefile b/src/windows/identity/Makefile
new file mode 100644
index 0000000000..3a79ee5f58
--- /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 <config/Makefile.w32>
+
+!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 0000000000..686a044ce5
--- /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 <Makefile.w32>
+
+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 0000000000..1b862a9ad4
--- /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 <Win32.Mak>
+
+# 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 0000000000..c6c82814f2
--- /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 <input> which is the .csv file
+#to be parsed, a <config> which is a configuration file and the
+#<output>.
+
+# 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 <input-filename> <config-filename> <output-filename>\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(<IN>) {
+ 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 0000000000..ba3bf9bfce
--- /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 = <<EOS;
+/*
+This file was autogenerated from:
+ $cfgfn
+ $infn
+
+Do not modify directly.
+*/
+#include<kconfig.h>
+
+kconf_schema schema_$inbase\[] = {
+EOS
+
+$record_prefix = "{";
+
+$record_sep = ",\n";
+
+$record_postfix = "}";
+
+$file_postfix = <<EOS;
+
+};
+
+
+EOS
+
+$skip_lines = 1;
+
+@pquote = (1,0,0,1);
+
+sub rec_handler {
+ $arr = shift;
+ if($$arr[1] =~ "KC_STRING") {
+ $$arr[2] = "(khm_int64) L\"".$$arr[2]."\"";
+ $$arr[2] =~ s/\[\~\]/\\0/g;
+ }
+
+ if($#$arr == 2){
+ $$arr[3] = "";
+ }
+}
+
+$record_parser = \&rec_handler;
diff --git a/src/windows/identity/doc/Makefile b/src/windows/identity/doc/Makefile
new file mode 100644
index 0000000000..71e9f0f16f
--- /dev/null
+++ b/src/windows/identity/doc/Makefile
@@ -0,0 +1,68 @@
+#
+# 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=doc
+!include <../config/Makefile.w32>
+
+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 0000000000..1adb3b8f58
--- /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 0000000000..3257520e1f
--- /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 0000000000..e8f7d29993
--- /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 0000000000..a1b2c2cc27
--- /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 0000000000..5e844833f4
--- /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 0000000000..7aac8021b5
--- /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 <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> 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 0000000000..13314c2b04
--- /dev/null
+++ b/src/windows/identity/doc/footer.html
@@ -0,0 +1,19 @@
+<hr size="1">
+
+<table width="100%" border="0">
+ <tr>
+ <td>
+ <address style="align:right;">
+ <small>Generated on $datetime for $projectname $projectnumber by&nbsp;<a href="http://www.doxygen.org/index.html">Doxygen</a> $doxygenversion<br>
+ &copy; 2004 Massachusetts Institute of Technology. Contact <a href="mailto:khimaira@mit.edu">khimaira@mit.edu</a><br>
+ </small>
+ </address>
+ </td>
+ <td width="100" align="right">
+ <img src="khimaira_logo_small.png" border="0">
+ </td>
+ </tr>
+</table>
+
+</body>
+</html>
diff --git a/src/windows/identity/doc/header.html b/src/windows/identity/doc/header.html
new file mode 100644
index 0000000000..4235468f36
--- /dev/null
+++ b/src/windows/identity/doc/header.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
+<title>$title</title>
+<link href="$relpath$stylesheet.css" rel="stylesheet" type="text/css">
+</head><body>
diff --git a/src/windows/identity/doc/images/Thumbs.db b/src/windows/identity/doc/images/Thumbs.db
new file mode 100644
index 0000000000..371f5d62e6
--- /dev/null
+++ b/src/windows/identity/doc/images/Thumbs.db
Binary files differ
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 0000000000..d06ca9f88c
--- /dev/null
+++ b/src/windows/identity/doc/images/credview-select-outline.jpg
Binary files differ
diff --git a/src/windows/identity/doc/images/khimaira_logo.png b/src/windows/identity/doc/images/khimaira_logo.png
new file mode 100644
index 0000000000..26c338007d
--- /dev/null
+++ b/src/windows/identity/doc/images/khimaira_logo.png
Binary files differ
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 0000000000..10e8fde4db
--- /dev/null
+++ b/src/windows/identity/doc/images/khimaira_logo_old.jpg
Binary files differ
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 0000000000..26c338007d
--- /dev/null
+++ b/src/windows/identity/doc/images/khimaira_logo_small.png
Binary files differ
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 0000000000..94d8d19113
--- /dev/null
+++ b/src/windows/identity/doc/images/khimaira_logo_small_old.jpg
Binary files differ
diff --git a/src/windows/identity/doc/main_page.h b/src/windows/identity/doc/main_page.h
new file mode 100644
index 0000000000..545e9ee174
--- /dev/null
+++ b/src/windows/identity/doc/main_page.h
@@ -0,0 +1,108 @@
+/*
+ * 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$ */
+
+/*! \mainpage NetIDMgr
+
+ \image html khimaira_logo.png
+
+ \section main_dev Documentation for Developers
+
+ NetIDMgr is a credentials manager, which currently manages
+ Kerberos IV, Kerberos V and AFS credentials. This document
+ describes the API that is implemented by the NetIDMgr system.
+
+ See the following sections for more information :
+ - \subpage license
+ - \subpage bugs
+ - \subpage releases
+
+ &copy; 2004 Massachusetts Institute of Technology
+*/
+
+/*!
+ \page license License agreement and credits
+
+ NetIDMgr is distributed under the MIT License.
+
+ \section license_l MIT License
+
+ Copyright &copy; 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.
+
+ \section license_credits Credits
+
+ NetIDMgr was developed at the Massachusetts Institute of
+ Technology.
+
+ (Contributor list goes here)
+
+ <a href="http://web.mit.edu/is">Information Services and
+ Technology</a> at <a href="http://web.mit.edu">Massachusetts
+ Institute of Technology</a>
+*/
+
+/*! \page bugs Reporting bugs
+
+ NetIDMgr bugs can be reported to
+ <a href="mailto:khimaira@mit.edu">khimaira@mit.edu</a> 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
+
+ - <b>0.1.1</b> (Charles Manson) <em>[soon]</em>\n
+ First alpha release. As stable as Charles Manson, hence the
+ name.
+
+ - <b>0.1.2</b> (tbd) <em>[tbd]</em>\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 0000000000..dbf1600803
--- /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 0000000000..3cb65a4221
--- /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 0000000000..ed8d038e20
--- /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 0000000000..8c57b0300e
--- /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 0000000000..fc3e0624db
--- /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 0000000000..ab3848ed3b
--- /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 0000000000..8ef325049d
--- /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 <b>current selection</b>).
+
+ - The current credential or selection which the cursor is on (the
+ <b>cursor selection</b>).
+
+ 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 0000000000..0f9ab661c4
--- /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 0000000000..c7a95a3640
--- /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 0000000000..2e24f6f3ec
--- /dev/null
+++ b/src/windows/identity/help/Index.hhk
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<HTML>
+<HEAD>
+<meta name="GENERATOR" content="Microsoft&reg; HTML Help Workshop 4.1">
+<!-- Sitemap 1.0 -->
+</HEAD><BODY>
+<UL>
+</UL>
+</BODY></HTML>
diff --git a/src/windows/identity/help/Makefile b/src/windows/identity/help/Makefile
new file mode 100644
index 0000000000..2b823d85a6
--- /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 0000000000..01828e4deb
--- /dev/null
+++ b/src/windows/identity/help/html/images/Thumbs.db
Binary files differ
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 0000000000..1af792f08d
--- /dev/null
+++ b/src/windows/identity/help/html/images/link.GIF
Binary files differ
diff --git a/src/windows/identity/help/html/khm.css b/src/windows/identity/help/html/khm.css
new file mode 100644
index 0000000000..82c4d57f76
--- /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 0000000000..2130df192f
--- /dev/null
+++ b/src/windows/identity/help/html/menu_exit.htm
@@ -0,0 +1,9 @@
+<html>
+<head>
+ <title>title</title>
+ <meta name="description" content="">
+ <meta name="keywords" content="">
+ <link rel="stylesheet" type="text/css" href="khm.css">
+</head>
+
+</html> \ 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 0000000000..021f71f5a4
--- /dev/null
+++ b/src/windows/identity/help/html/menu_file.htm
@@ -0,0 +1,18 @@
+<html>
+<head>
+ <title>File menu</title>
+ <meta name="description" content="">
+ <meta name="keywords" content="">
+ <link rel="stylesheet" type="text/css" href="khm.css">
+</head>
+
+<h1>File menu</h1>
+
+<p>Menu items</p>
+
+<ul>
+<li><a href="menu_properties.htm">Properties...</a></li>
+<li><a href="menu_exit.htm">Exit...</a></li>
+</ul>
+
+</html> \ 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 0000000000..2130df192f
--- /dev/null
+++ b/src/windows/identity/help/html/menu_properties.htm
@@ -0,0 +1,9 @@
+<html>
+<head>
+ <title>title</title>
+ <meta name="description" content="">
+ <meta name="keywords" content="">
+ <link rel="stylesheet" type="text/css" href="khm.css">
+</head>
+
+</html> \ 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 0000000000..2130df192f
--- /dev/null
+++ b/src/windows/identity/help/html/template.htm
@@ -0,0 +1,9 @@
+<html>
+<head>
+ <title>title</title>
+ <meta name="description" content="">
+ <meta name="keywords" content="">
+ <link rel="stylesheet" type="text/css" href="khm.css">
+</head>
+
+</html> \ 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 0000000000..32d7d05b26
--- /dev/null
+++ b/src/windows/identity/help/html/welcome.htm
@@ -0,0 +1,24 @@
+<html>
+<head>
+ <title>Welcome to Khimaira</title>
+ <meta name="description" content="Welcome">
+ <meta name="keywords" content="welcome">
+ <link rel="stylesheet" type="text/css" href="khm.css">
+</head>
+
+<h1>Welcome to Khimaira</h1>
+
+<p>Khimaira is a credentials manager that lets you manage Kerberos,
+AFS and other types of credentials.
+</p>
+
+<p>The following web sites provide more information about Kerberos and
+AFS:</p>
+
+<ul>
+<li><a class="external" href="http://web.mit.edu/kerberos">http://web.mit.edu/kerbeors</a></li>
+<li><a class="external" href="http://openafs.org">http://openafs.org</a></li>
+</ul>
+
+
+</html> \ 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 0000000000..4ffa6d8f59
--- /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 0000000000..8e0d5a5979
--- /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 0000000000..cde5119a3f
--- /dev/null
+++ b/src/windows/identity/help/toc.hhc
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<HTML>
+<HEAD>
+<meta name="GENERATOR" content="Microsoft&reg; HTML Help Workshop 4.1">
+<!-- Sitemap 1.0 -->
+</HEAD><BODY>
+<OBJECT type="text/site properties">
+ <param name="Category" value="Concepts">
+ <param name="CategoryDesc" value="Authentication, authorization and related concepts.">
+ <param name="Category" value="Usage">
+ <param name="CategoryDesc" value="Usage instructions for Khimaira">
+ <param name="Window Styles" value="0x800025">
+</OBJECT>
+<UL>
+ <LI> <OBJECT type="text/sitemap">
+ <param name="Name" value="Welcome to Khimaira">
+ <param name="Local" value="html\welcome.htm">
+ </OBJECT>
+ <LI> <OBJECT type="text/sitemap">
+ <param name="Name" value="Using Khimaira">
+ </OBJECT>
+ <UL>
+ <LI> <OBJECT type="text/sitemap">
+ <param name="Name" value="Menus">
+ </OBJECT>
+ <UL>
+ <LI> <OBJECT type="text/sitemap">
+ <param name="Name" value="File...">
+ <param name="Local" value="html\menu_file.htm">
+ </OBJECT>
+ </UL>
+ <LI> <OBJECT type="text/sitemap">
+ <param name="Name" value="Actions">
+ </OBJECT>
+ <UL>
+ <LI> <OBJECT type="text/sitemap">
+ <param name="Name" value="Properties">
+ <param name="Local" value="html\menu_properties.htm">
+ </OBJECT>
+ <LI> <OBJECT type="text/sitemap">
+ <param name="Name" value="Exit">
+ <param name="Local" value="html\menu_exit.htm">
+ </OBJECT>
+ </UL>
+ </UL>
+</UL>
+</BODY></HTML>
diff --git a/src/windows/identity/include/Makefile b/src/windows/identity/include/Makefile
new file mode 100644
index 0000000000..17182d57a1
--- /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 0000000000..427926306b
--- /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<stddef.h>
+#include<limits.h>
+#include<wchar.h>
+
+/*!\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 <tt>s, s+t, s+2*t,
+ s+3*t, ...</tt> 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 0000000000..d56fa7dc73
--- /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 0000000000..330cfc498d
--- /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 0000000000..8348bbf95e
--- /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 0000000000..b7354c4688
--- /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 0000000000..26619c011c
--- /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 0000000000..3d69c998e5
--- /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<kconfiginternal.h>
+#include<assert.h>
+
+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;i<c->nSchema;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;i<c->nSchema;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;i<c->nSchema;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; i<c->nSchema; 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; i<c->nSchema; 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 0000000000..22d923bd6f
--- /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<khdefs.h>
+#include<mstring.h>
+
+/*! \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 0000000000..1b4b70170c
--- /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<windows.h>
+#include<strsafe.h>
+#include<kconfig.h>
+#include<khlist.h>
+#include<kherror.h>
+#include<utils.h>
+
+/* 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 0000000000..8a9d6578c9
--- /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<kconfiginternal.h>
+
+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 0000000000..4a7b46682b
--- /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<kconfiginternal.h>
+
diff --git a/src/windows/identity/kconfig/test/utiltest.c b/src/windows/identity/kconfig/test/utiltest.c
new file mode 100644
index 0000000000..0652c63b98
--- /dev/null
+++ b/src/windows/identity/kconfig/test/utiltest.c
@@ -0,0 +1,207 @@
+#include<stdio.h>
+#include<kconfig.h>
+#include<strsafe.h>
+
+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<n_strings; i++) {
+ cbbuf = sizeof(wbuf);
+ printf("Multi string:[");
+ print_ms(strings[i].ms);
+ printf("]->");
+ 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<n_strings; i++) {
+ cbbuf = sizeof(wbuf);
+ printf("CSV:[%S]->", 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<n_strings; i++) {
+ cbbuf = sizeof(wbuf);
+ khc_csv_to_multi_string(wbuf, &cbbuf, strings[i].csv);
+
+ printf("MS[");
+ print_ms(wbuf);
+ printf("] + [foo]=[");
+
+ cbbuf = sizeof(wbuf);
+ code = khc_multi_string_append(wbuf, &cbbuf, L"foo");
+
+ if(code) {
+ printf(" returned %d\n", code);
+ return code;
+ }
+
+ print_ms(wbuf);
+ printf("]\n");
+
+ printf(" byte length: %d\n", cbbuf);
+ }
+ return code;
+}
+
+int ms_delete_test(void)
+{
+ int code = 0;
+ wchar_t wbuf[512];
+ int i;
+ size_t cbs;
+
+ printf("khc_multi_string_delete() test:\n");
+ for(i=0; i<n_strings; i++) {
+ cbs = sizeof(wbuf);
+ khc_csv_to_multi_string(wbuf, &cbs, strings[i].csv);
+
+ printf("MS[");
+ print_ms(wbuf);
+ printf("] - [b]=[");
+
+ printf("cs:");
+ code = khc_multi_string_delete(wbuf, L"b", KHC_CASE_SENSITIVE);
+ if(code) {
+ printf("ci:");
+ code = khc_multi_string_delete(wbuf, L"b", 0);
+ }
+ if(code) {
+ printf("pcs:");
+ code = khc_multi_string_delete(wbuf, L"b", KHC_CASE_SENSITIVE | KHC_PREFIX);
+ }
+ if(code) {
+ printf("pci:");
+ code = khc_multi_string_delete(wbuf, L"b", KHC_PREFIX);
+ }
+
+ if(!code)
+ print_ms(wbuf);
+ else
+ printf(" returned %d\n", code);
+
+ printf("]\n");
+ }
+
+ return code;
+}
+
+int main(int argc, char ** argv) {
+
+ if(ms_to_csv_test())
+ return 1;
+
+ if(csv_to_ms_test())
+ return 1;
+
+ if(ms_append_test())
+ return 1;
+
+ if(ms_delete_test())
+ return 1;
+
+ return 0;
+}
diff --git a/src/windows/identity/kcreddb/Makefile b/src/windows/identity/kcreddb/Makefile
new file mode 100644
index 0000000000..24cc0339a2
--- /dev/null
+++ b/src/windows/identity/kcreddb/Makefile
@@ -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.
+
+
+MODULE=kcreddb
+!include <../config/Makefile.w32>
+
+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 0000000000..e43540dc51
--- /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<kcreddbinternal.h>
+#include<assert.h>
+
+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 0000000000..5199aec926
--- /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 0000000000..0f50be25d0
--- /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<kcreddbinternal.h>
+#include<assert.h>
+
+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 0000000000..3ff1f041dd
--- /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 0000000000..1fe1dcd973
--- /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<kcreddbinternal.h>
+#include<assert.h>
+
+/* 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;i<KCDB_ATTR_MAX_ID;i++) {
+ if(kcdb_cred_val_exist(src, i)) {
+ /*NOTE: the logic here has to reflect the logic in
+ kcdb_cred_set_attr() */
+ if(KHM_FAILED(kcdb_attrib_get_info(i, &a)))
+ continue;
+
+ if((a->flags & 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 0000000000..8104f686b4
--- /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 0000000000..3a39d48a63
--- /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<kcreddbinternal.h>
+#include<assert.h>
+
+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;i<cs->nclist;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; i<ncl1; i++)
+ if(cl1[i]) {
+ for(j=0; j<ncl2; j++)
+ if(cl2[j] && kcdb_creds_is_equal((khm_handle) cl1[i], (khm_handle) cl2[j])) {
+ /* they are equivalent. make them equal */
+
+ /* depending on whether any changes were made,
+ update ldelta with the proper bit flag */
+
+ rv = kcdb_cred_update(cl1[i], cl2[j]);
+ if (rv == KHM_ERROR_SUCCESS) {
+ kcdb_credset_update_cred_ref((khm_handle) cs1, (khm_handle) cl1[i]);
+ ldelta |= KCDB_DELTA_MODIFY;
+ }
+
+ cl2[j] = NULL;
+ cl1[i] = NULL;
+ break;
+ }
+ }
+
+ /* all the creds that are left in cl1 need to be removed */
+ for(i=0; i<ncl1; i++)
+ if(cl1[i]) {
+ kcdb_credset_del_cred_ref((khm_handle) cs1, (khm_handle) cl1[i]);
+ cl1[i] = NULL;
+ ldelta |= KCDB_DELTA_DEL;
+ }
+
+ /* all the creds in cl2 need to be added to cs1 */
+ for(j=0; j<ncl2; j++)
+ if(cl2[j]) {
+ /* duplicate the credential and add it if we are adding it to the
+ root credential store. */
+ if(cs1 == kcdb_root_credset) {
+ khm_handle h;
+
+ if(KHM_SUCCEEDED(kcdb_cred_dup((khm_handle) cl2[j], &h))) {
+ kcdb_credset_add_cred((khm_handle) cs1, h, -1);
+ kcdb_cred_release(h);
+ }
+ } else
+ kcdb_credset_add_cred((khm_handle) cs1, cl2[j], -1);
+ cl2[j] = NULL;
+ ldelta |= KCDB_DELTA_ADD;
+ }
+
+ if(delta)
+ *delta = ldelta;
+
+ if((cs1 == kcdb_root_credset) && ldelta) {
+ /* something changed in the root credential set */
+ kmq_post_message(KMSG_CRED,KMSG_CRED_ROOTDELTA,ldelta,NULL);
+ }
+ return KHM_ERROR_SUCCESS;
+}
+
+KHMEXP khm_int32 KHMAPI kcdb_credset_collect(
+ khm_handle cs_dest,
+ khm_handle cs_src,
+ khm_handle identity,
+ khm_int32 type,
+ 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;
+
+ 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(identity && !kcdb_is_active_identity(identity))
+ return KHM_ERROR_INVALID_PARM;
+
+ if(cs_src)
+ cs = (kcdb_credset *) cs_src;
+ else
+ cs = kcdb_root_credset;
+
+ if(cs_dest)
+ rcs = (kcdb_credset *) cs_dest;
+ else
+ rcs = kcdb_root_credset;
+
+ if (kcdb_credset_is_sealed(rcs))
+ return KHM_ERROR_INVALID_OPERATION;
+
+ EnterCriticalSection(&(cs->cs));
+ 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; i<rcs->nclist; 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; i<cs->nclist; 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; i<rcs->nclist; 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; i<cs->nclist; 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;i<cs->nclist;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; i<cs->nclist; 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(i<cs->nclist)
+ 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; i<cs->nclist; i++) {
+ if(cs->clist[i].cred == cred)
+ break;
+ }
+
+ if(i<cs->nclist) {
+ 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; i<cs->nclist; i++) {
+ if(cs->clist[i].cred == cred)
+ break;
+ }
+
+ if(i<cs->nclist) {
+ 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; i<o->nFields; 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 0000000000..c19246540d
--- /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 0000000000..dc2b7b85a0
--- /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<kcreddbinternal.h>
+
+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 0000000000..6e46db303e
--- /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 0000000000..d6ae129d6c
--- /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<kcreddbinternal.h>
+#include<assert.h>
+
+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 0000000000..6c7e26ee86
--- /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 0000000000..2df3f3e3f9
--- /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<kcreddbinternal.h>
+
+/* 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 0000000000..bd1fc6f330
--- /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 0000000000..e5be0f4629
--- /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<khdefs.h>
+#include<time.h>
+
+
+/*! \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 &lt; \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 &gt; \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 0000000000..699954cf01
--- /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<windows.h>
+#include<kcreddb.h>
+#include<kmq.h>
+#include<khlist.h>
+#include<utils.h>
+#include<kherror.h>
+#include<khmsgtypes.h>
+#include<kconfig.h>
+#include<strsafe.h>
+
+#include<langres.h>
+
+#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 0000000000..796084a995
--- /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<kcreddbinternal.h>
+
+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 0000000000..2f733199a7
--- /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 0000000000..ab6620cd51
--- /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 0000000000..dfb47e0d43
--- /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 0000000000..e41694544b
--- /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<kcreddbinternal.h>
+#include<limits.h>
+
+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 0000000000..9d8b52e252
--- /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 0000000000..84f9da5baa
--- /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 0000000000..04b45238b4
--- /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<kherrinternal.h>
+#include<assert.h>
+#ifdef Debug
+#include<stdarg.h>
+#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; i<n_ctx_handlers; i++) {
+ if (ctx_handlers[i].filter & e)
+ ctx_handlers[i].h(e,c);
+ }
+}
+
+void attach_this_thread(void) {
+ kherr_thread * t;
+
+ t = (kherr_thread *) TlsGetValue(tls_error);
+ if (t)
+ return;
+
+ t = malloc(sizeof(kherr_thread) +
+ sizeof(kherr_context *) * THREAD_STACK_SIZE);
+ t->nc_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 0000000000..7ccc6e5609
--- /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<khdefs.h>
+#include<khlist.h>
+
+/*! \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:
+
+ <b>KHERR_CTX_BEGIN</b>: 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.
+
+ <b>KHERR_CTX_DESCRIBE</b>: 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.
+
+ <b>KHERR_CTX_ERROR</b>: 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.
+
+ <b>KHERR_CTX_END</b>: 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.
+
+ <b>Allocated strings</b>: 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.
+
+ <b>String resources</b>: 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.
+
+ <b>Message resources</b>: 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.
+
+ <b>Formatted fields</b>: 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 0000000000..e91cc3d018
--- /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<windows.h>
+#include<kherr.h>
+#include<strsafe.h>
+
+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 0000000000..b108609db4
--- /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<kherrinternal.h>
+
+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 0000000000..6135cdc4f0
--- /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 0000000000..af8419fdaa
--- /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<kmminternal.h>
+
+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; i<n_locales; i++) {
+ if(locales[i].language == lcid) {
+ f[i] = TRUE;
+ if(kmm_load_locale_lib(m, &locales[i]))
+ break;
+ }
+ }
+
+ if(i<n_locales)
+ goto _exit;
+
+ /* ok, that didn't work. Try an inexact match. */
+ for(i=0; i<n_locales; i++) {
+ if(!f[i] && (PRIMARYLANGID(locales[i].language) == PRIMARYLANGID(lcid))) {
+ f[i] = TRUE;
+ if(kmm_load_locale_lib(m,&locales[i]))
+ break;
+ }
+ }
+
+ if(i < n_locales)
+ goto _exit;
+
+ /* hmm. no matches yet. just try to locate the default locale */
+ for(i=0; i<n_locales; i++) {
+ if(!f[i] && (locales[i].flags & KMM_MLOC_FLAG_DEFAULT)) {
+ f[i] = TRUE;
+ if(kmm_load_locale_lib(m,&locales[i]))
+ break;
+ }
+ }
+
+ if(i < n_locales)
+ goto _exit;
+
+ /* give up */
+ rv = KHM_ERROR_NOT_FOUND;
+
+_exit:
+ free(f);
+ return rv;
+}
+
+#ifdef _WIN32
+KHMEXP HMODULE KHMAPI kmm_get_resource_hmodule(kmm_module vm)
+{
+ if(!kmm_is_module(vm))
+ return NULL;
+ else
+ return (kmm_module_from_handle(vm))->h_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 0000000000..8e487be73a
--- /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<khdefs.h>
+#include<kmq.h>
+
+/*! \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 0000000000..e1f5292ce8
--- /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<kmminternal.h>
+
+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 0000000000..f37eaa186c
--- /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<kmminternal.h>
+
+/* 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; i<pi->n_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 0000000000..ea13fc19bd
--- /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<kmminternal.h>
+
+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; i<module->n_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 0000000000..3c690f36b6
--- /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<kmminternal.h>
+
+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 <KMSG_KMM,KMSG_KMM_I_REG> 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; i<pd->n_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 0000000000..93444bdf4b
--- /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,<module name>,The name of the module that registered this plugin
+ Description,KC_STRING,<Description>,Description of the plugin
+ Dependencies,KC_STRING,<Dependencies>,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 library binary>,Path to the DLL
+ Description,KC_STRING,<Description>,Description of the module
+ Vendor,KC_STRING,<Vendor 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,<plugins>,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 0000000000..662eff228f
--- /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<windows.h>
+#include<strsafe.h>
+
+#define KHERR_FACILITY kmm_facility
+#define KHERR_FACILITY_ID KHM_FACILITY_KMM
+#define KHERR_HMODULE ((HMODULE) kmm_hInstance)
+#include<kherr.h>
+
+#include<kmm.h>
+#include<khmsgtypes.h>
+#include<kherror.h>
+#include<kplugin.h>
+#include<utils.h>
+#include<kconfig.h>
+#include<kcreddb.h>
+#include<kmm_msgs.h>
+
+
+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 0000000000..8ec4bc0a3c
--- /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<kmminternal.h>
+
+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 0000000000..f7489bf4c0
--- /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<kmm.h>
+#include<kherror.h>
+
+/*! \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 0000000000..5e88121d14
--- /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 0000000000..1f11e0fe42
--- /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 0000000000..32072cf637
--- /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<kmqinternal.h>
+#include<assert.h>
+
+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 0000000000..cb50c54761
--- /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<kmqinternal.h>
+#include<kconfig.h>
+#include<assert.h>
+
+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 0000000000..3d596d7954
--- /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<khdefs.h>
+#include<khlist.h>
+#include<kherr.h>
+
+/* 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 <KMSG_SYSTEM,KMSG_SYSTEM_EXIT>
+*/
+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 <KMSG_SYSTEM,KMSG_SYSTEM_EXIT> 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 <KMSG_SYSTEM,KMSG_SYSTEM_EXIT> 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 0000000000..c6d5ca451c
--- /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 0000000000..c82fb925d7
--- /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<windows.h>
+#include<kmq.h>
+#include<khlist.h>
+#include<kherror.h>
+#include<khmsgtypes.h>
+#include<kconfig.h>
+#include<strsafe.h>
+
+#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 0000000000..d93403a57b
--- /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<kmqinternal.h>
+
+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 0000000000..eb44eecf3a
--- /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<kmqinternal.h>
+
+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;i<KMQ_MSG_TYPE_MAX;i++) {
+ if(msg_types[i])
+ kmqint_free_msg_type(i);
+ }
+ LeaveCriticalSection(&cs_kmq_types);
+ DeleteCriticalSection(&cs_kmq_types);
+}
+
+/*! \internal
+ \brief Notifies that the message has completed
+
+ \return Zero if the completion handling is done. Nonzero if the
+ handling is queued.
+ */
+int kmqint_notify_msg_completion(kmq_message * m) {
+ kmq_msg_type * mt;
+ kmq_msg_completion_handler h;
+
+ /* doing it this way to elude race conditions without
+ obtaining a lock */
+
+ mt = msg_types[m->type];
+ 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 0000000000..5391844ac1
--- /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<kmqinternal.h>
+
+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;i<n_subs;i++) {
+ kmqint_post((kmq_msg_subscription *) subs[i], 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_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 0000000000..5d48c2d12d
--- /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 0000000000..f54d4ef72c
--- /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<windows.h>
+
+/* 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 0000000000..10870e8c39
--- /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<afxres.h>
+#include<khimaira_version.h>
+
+#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 0000000000..cbadbc6448
--- /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 0000000000..cd33813f78
--- /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<windows.h>
+#include<khdefs.h>
+#include<kherror.h>
+#include<dynimport.h>
+
+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 0000000000..b3ba225a66
--- /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<khdefs.h>
+#include<tlhelp32.h>
+#include<ntsecapi.h>
+
+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 <loadfuncs-com_err.h>
+#include <loadfuncs-krb5.h>
+#include <loadfuncs-profile.h>
+#include <loadfuncs-krb.h>
+#include <loadfuncs-krb524.h>
+#include <loadfuncs-lsa.h>
+
+//// 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 0000000000..5501a12068
--- /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<windows.h>
+#include<kcreddb.h>
+#include<kherror.h>
+#include<dynimport.h>
+
+/**************************************/
+/* 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 0000000000..7d998215a5
--- /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<krb5.h>
+
+#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 0000000000..d6b749192d
--- /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 0000000000..9c7048e05e
--- /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 0000000000..9feaad2a70
--- /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<krbcred.h>
+#include<kherror.h>
+
+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 0000000000..be8f4e7c57
--- /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 <errno.h>
+#include <com_err.h>
+/*
+ * 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 <krberr.h>
+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 <kadm_err.h>
+
+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 <stdarg.h>
+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 0000000000..9ad3406980
--- /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<krbcred.h>
+#include<kherror.h>
+#include<khuidefs.h>
+#include<strsafe.h>
+
+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 0000000000..8fda720b3a
--- /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<krbcred.h>
+#include<kherror.h>
+
+#define SECURITY_WIN32
+#include <security.h>
+#include <ntsecapi.h>
+
+#include <string.h>
+#include <time.h>
+#include <assert.h>
+#include <strsafe.h>
+
+
+
+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, &sections);
+
+ 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 0000000000..ea97358b9f
--- /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<stdlib.h>
+#include<krb5.h>
+
+#include <windows.h>
+#define SECURITY_WIN32
+#include <security.h>
+#include <ntsecapi.h>
+
+#include <krb5common.h>
+
+#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 0000000000..106febac00
--- /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<krbcred.h>
+#include<kherror.h>
+#include<khmsgtypes.h>
+#include<khuidefs.h>
+#include<commctrl.h>
+#include<strsafe.h>
+#include<krb5.h>
+
+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(&reg, 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, &reg);
+
+ 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 0000000000..bed0d1ccb9
--- /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 0000000000..e56d114ee9
--- /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<windows.h>
+
+#include<khdefs.h>
+#include<kcreddb.h>
+#include<kmm.h>
+#include<kconfig.h>
+
+
+#include<krb4funcs.h>
+#include<krb5common.h>
+#include<errorfuncs.h>
+#include<dynimport.h>
+
+#include<langres.h>
+#include<datarep.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_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 0000000000..a5d62a26a4
--- /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 0000000000..2096adec3b
--- /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 0000000000..60ceb7f83c
--- /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<krbcred.h>
+#include<kherror.h>
+
+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 0000000000..9bf9ef020e
--- /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 0000000000..f8cc4cc484
--- /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<krbcred.h>
+#include<krb5.h>
+#include<kherror.h>
+#include<strsafe.h>
+
+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 0000000000..e5388f01db
--- /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 0000000000..ab64889cb7
--- /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<krbcred.h>
+#include<kherror.h>
+
+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 0000000000..46d68f9fc0
--- /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 <errno.h>
+#include <com_err.h>
+/*
+ * 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 <krberr.h>
+#include <kadm_err.h>
+
+#define kadm_err_base ERROR_TABLE_BASE_kadm
+
+#include <stdarg.h>
+
+#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 0000000000..c3b00e1612
--- /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<krbcred.h>
+#include<krb5.h>
+#include<assert.h>
+#include<lm.h>
+
+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(&reg, 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, &reg);
+
+ if (KHM_FAILED(khui_cfg_open(NULL, L"Kerberos5", &node))) {
+ node = NULL;
+#ifdef DEBUG
+ assert(FALSE);
+#endif
+ }
+
+ ZeroMemory(&reg, 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, &reg);
+
+ khui_cfg_release(node);
+
+ if (KHM_FAILED(khui_cfg_open(NULL, L"KhmIdentities", &node))) {
+ node = NULL;
+#ifdef DEBUG
+ assert(FALSE);
+#endif
+ }
+
+ ZeroMemory(&reg, 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, &reg);
+
+ ZeroMemory(&reg, 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, &reg);
+
+ 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 0000000000..d3c97fff2f
--- /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<krbcred.h>
+#include<kherror.h>
+
+#define SECURITY_WIN32
+#include <security.h>
+#include <ntsecapi.h>
+
+#include <string.h>
+#include <time.h>
+#include <assert.h>
+#include <strsafe.h>
+
+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; n<list->addrCount; 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;i<addr_count;i++ ) {
+ if ( addrs[i] ) {
+ if ( addrs[i]->contents )
+ 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,
+ &sections);
+
+ 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 0000000000..79ca95646a
--- /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<stdlib.h>
+#include<krb5.h>
+
+#include <windows.h>
+#define SECURITY_WIN32
+#include <security.h>
+#include <ntsecapi.h>
+
+#include <krb5common.h>
+
+#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 0000000000..c568e49d03
--- /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<krbcred.h>
+#include<kherror.h>
+#include<khmsgtypes.h>
+#include<commctrl.h>
+#include<strsafe.h>
+#include<krb5.h>
+#include<assert.h>
+
+#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 0000000000..968e0e2903
--- /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<krbcred.h>
+#include<kherror.h>
+#include<khmsgtypes.h>
+#include<strsafe.h>
+#include<krb5.h>
+
+#include<assert.h>
+
+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; i<num_prompts; i++) {
+ krb5_data * d;
+ wchar_t wbuf[512];
+ khm_size cbbuf;
+ size_t cch;
+
+ d = prompts[i].reply;
+
+ cbbuf = sizeof(wbuf);
+ if(KHM_SUCCEEDED(khui_cw_get_prompt_value(nc, i, wbuf, &cbbuf))) {
+ UnicodeStrToAnsi(d->data, 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 0000000000..4b53ed3e8f
--- /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<krbcred.h>
+#include<kherror.h>
+#include<khmsgtypes.h>
+#include<commctrl.h>
+#include<strsafe.h>
+#include<krb5.h>
+
+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 0000000000..9134de2925
--- /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<krbcred.h>
+#include<kherror.h>
+#include<khmsgtypes.h>
+#include<commctrl.h>
+#include<strsafe.h>
+#include<krb5.h>
+
+/* 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 0000000000..b892531afc
--- /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 <windows.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <winsock.h>
+#include "leashdll.h"
+#include <KerberosIV/krb.h>
+#include <prot.h>
+#include <time.h>
+
+#include <leashwin.h>
+#include "leasherr.h"
+#include "leash-int.h"
+#include "leashids.h"
+
+#include <mitwhich.h>
+
+#include <winkrbid.h>
+#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; i<krb5_princ_realm(ctx, me)->length; 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; n<killList->addrCount; 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; i<krb5_princ_realm(ctx, princ)->length; 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
+ * <title><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 0000000000..c577eec3b9
--- /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 0000000000..08978f11fd
--- /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 0000000000..087b93e47c
--- /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 0000000000..22f973f939
--- /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 0000000000..87f74f547a
--- /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 0000000000..db996d951b
--- /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 0000000000..402fc5d885
--- /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 0000000000..2dde601a49
--- /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 0000000000..a427b7dd0d
--- /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 0000000000..41fca2d17e
--- /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 0000000000..37c7ba7d11
--- /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 0000000000..1cef2d7ce7
--- /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, &reg);
+
+ 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, &reg);
+ 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, &reg);
+ 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 0000000000..aafa12d723
--- /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 0000000000..16a344275c
--- /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 0000000000..22a41eeb5a
--- /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, &reg);
+ 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, &reg);
+#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, &reg);
+#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(&reg, 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,
+ &reg);
+ } 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);
+
+ 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, &reg);
+
+ 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);
+
+ 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);
+
+ 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);
+
+ 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, &reg);
+}
+
+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 0000000000..64da771532
--- /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 0000000000..b92e5d4a83
--- /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 0000000000..b25b6630eb
--- /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 0000000000..784a7f90b9
--- /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 = &rect;
+ 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 0000000000..66fc3d2fbc
--- /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 0000000000..e69de29bb2
--- /dev/null
+++ b/src/windows/identity/ui/htmlwnd.h
diff --git a/src/windows/identity/ui/htwnd.c b/src/windows/identity/ui/htwnd.c
new file mode 100644
index 0000000000..9acbac704e
--- /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, &param_start, &param_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 0000000000..9e74788038
--- /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 0000000000..a80265f867
--- /dev/null
+++ b/src/windows/identity/ui/images/Thumbs.db
Binary files differ
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 0000000000..8dcb29e7a6
--- /dev/null
+++ b/src/windows/identity/ui/images/app_notify_error.ico
Binary files differ
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 0000000000..8dcb29e7a6
--- /dev/null
+++ b/src/windows/identity/ui/images/app_notify_info.ico
Binary files differ
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 0000000000..8dcb29e7a6
--- /dev/null
+++ b/src/windows/identity/ui/images/app_notify_none.ico
Binary files differ
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 0000000000..8dcb29e7a6
--- /dev/null
+++ b/src/windows/identity/ui/images/app_notify_warn.ico
Binary files differ
diff --git a/src/windows/identity/ui/images/bitmap1.bmp b/src/windows/identity/ui/images/bitmap1.bmp
new file mode 100644
index 0000000000..1f07309b90
--- /dev/null
+++ b/src/windows/identity/ui/images/bitmap1.bmp
Binary files differ
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 0000000000..4fcdf843c8
--- /dev/null
+++ b/src/windows/identity/ui/images/cfg_applied.ico
Binary files differ
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 0000000000..d6e7f24e0d
--- /dev/null
+++ b/src/windows/identity/ui/images/cfg_default.ico
Binary files differ
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 0000000000..2ef11c7d11
--- /dev/null
+++ b/src/windows/identity/ui/images/cfg_deleted.ico
Binary files differ
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 0000000000..65e3691ffe
--- /dev/null
+++ b/src/windows/identity/ui/images/cfg_mod.ico
Binary files differ
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 0000000000..230ad36a40
--- /dev/null
+++ b/src/windows/identity/ui/images/chpw-dis-sm.bmp
Binary files differ
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 0000000000..43468cb2a9
--- /dev/null
+++ b/src/windows/identity/ui/images/chpw-dis.bmp
Binary files differ
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 0000000000..309a9178dc
--- /dev/null
+++ b/src/windows/identity/ui/images/chpw-sm.bmp
Binary files differ
diff --git a/src/windows/identity/ui/images/chpw.bmp b/src/windows/identity/ui/images/chpw.bmp
new file mode 100644
index 0000000000..688da40df3
--- /dev/null
+++ b/src/windows/identity/ui/images/chpw.bmp
Binary files differ
diff --git a/src/windows/identity/ui/images/disabled.ico b/src/windows/identity/ui/images/disabled.ico
new file mode 100644
index 0000000000..6b6a9078c1
--- /dev/null
+++ b/src/windows/identity/ui/images/disabled.ico
Binary files differ
diff --git a/src/windows/identity/ui/images/enabled.ico b/src/windows/identity/ui/images/enabled.ico
new file mode 100644
index 0000000000..22821a8b78
--- /dev/null
+++ b/src/windows/identity/ui/images/enabled.ico
Binary files differ
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 0000000000..0b4c920e61
--- /dev/null
+++ b/src/windows/identity/ui/images/flag-critical.bmp
Binary files differ
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 0000000000..f5be298a83
--- /dev/null
+++ b/src/windows/identity/ui/images/flag-warning.bmp
Binary files differ
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 0000000000..2815fb4486
--- /dev/null
+++ b/src/windows/identity/ui/images/flag_expired.bmp
Binary files differ
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 0000000000..49e560fb46
--- /dev/null
+++ b/src/windows/identity/ui/images/help-sm.bmp
Binary files differ
diff --git a/src/windows/identity/ui/images/help.bmp b/src/windows/identity/ui/images/help.bmp
new file mode 100644
index 0000000000..d7d4eafacc
--- /dev/null
+++ b/src/windows/identity/ui/images/help.bmp
Binary files differ
diff --git a/src/windows/identity/ui/images/icon1.ico b/src/windows/identity/ui/images/icon1.ico
new file mode 100644
index 0000000000..c2746b065c
--- /dev/null
+++ b/src/windows/identity/ui/images/icon1.ico
Binary files differ
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 0000000000..68850ab879
--- /dev/null
+++ b/src/windows/identity/ui/images/id-delete-dis-sm.bmp
Binary files differ
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 0000000000..11aedd5775
--- /dev/null
+++ b/src/windows/identity/ui/images/id-delete-dis.bmp
Binary files differ
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 0000000000..3447d57a1e
--- /dev/null
+++ b/src/windows/identity/ui/images/id-delete-sm.bmp
Binary files differ
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 0000000000..5b1b680300
--- /dev/null
+++ b/src/windows/identity/ui/images/id-delete.bmp
Binary files differ
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 0000000000..ff0c38e4d1
--- /dev/null
+++ b/src/windows/identity/ui/images/id-dis-sm.bmp
Binary files differ
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 0000000000..3c04aa2fa9
--- /dev/null
+++ b/src/windows/identity/ui/images/id-dis.bmp
Binary files differ
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 0000000000..d2a151c373
--- /dev/null
+++ b/src/windows/identity/ui/images/id-new-dis-sm.bmp
Binary files differ
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 0000000000..61c2fd21ab
--- /dev/null
+++ b/src/windows/identity/ui/images/id-new-dis.bmp
Binary files differ
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 0000000000..9fb17b98c3
--- /dev/null
+++ b/src/windows/identity/ui/images/id-new-sm.bmp
Binary files differ
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 0000000000..54830c1830
--- /dev/null
+++ b/src/windows/identity/ui/images/id-new.bmp
Binary files differ
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 0000000000..7d79343691
--- /dev/null
+++ b/src/windows/identity/ui/images/id-refresh-dis.bmp
Binary files differ
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 0000000000..c9d9addf15
--- /dev/null
+++ b/src/windows/identity/ui/images/id-refresh-sm-dis.bmp
Binary files differ
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 0000000000..11d1893b4a
--- /dev/null
+++ b/src/windows/identity/ui/images/id-refresh-sm.bmp
Binary files differ
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 0000000000..6357fde4f1
--- /dev/null
+++ b/src/windows/identity/ui/images/id-refresh.bmp
Binary files differ
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 0000000000..e507a62d5d
--- /dev/null
+++ b/src/windows/identity/ui/images/id-sm.bmp
Binary files differ
diff --git a/src/windows/identity/ui/images/id.bmp b/src/windows/identity/ui/images/id.bmp
new file mode 100644
index 0000000000..72a93c6a94
--- /dev/null
+++ b/src/windows/identity/ui/images/id.bmp
Binary files differ
diff --git a/src/windows/identity/ui/images/id.ico b/src/windows/identity/ui/images/id.ico
new file mode 100644
index 0000000000..1f0f676ffd
--- /dev/null
+++ b/src/windows/identity/ui/images/id.ico
Binary files differ
diff --git a/src/windows/identity/ui/images/ident.png b/src/windows/identity/ui/images/ident.png
new file mode 100644
index 0000000000..904f0239bc
--- /dev/null
+++ b/src/windows/identity/ui/images/ident.png
Binary files differ
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 0000000000..4257a1ad9e
--- /dev/null
+++ b/src/windows/identity/ui/images/import-dis.bmp
Binary files differ
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 0000000000..b9398c3849
--- /dev/null
+++ b/src/windows/identity/ui/images/import-sm-dis.bmp
Binary files differ
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 0000000000..0c9916877c
--- /dev/null
+++ b/src/windows/identity/ui/images/import-sm.bmp
Binary files differ
diff --git a/src/windows/identity/ui/images/import.bmp b/src/windows/identity/ui/images/import.bmp
new file mode 100644
index 0000000000..6a428c979e
--- /dev/null
+++ b/src/windows/identity/ui/images/import.bmp
Binary files differ
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 0000000000..d20b9eb3d8
--- /dev/null
+++ b/src/windows/identity/ui/images/khimaira-cfg.bmp
Binary files differ
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 0000000000..2e36b9bf20
--- /dev/null
+++ b/src/windows/identity/ui/images/logo_shade.bmp
Binary files differ
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 0000000000..8dcb29e7a6
--- /dev/null
+++ b/src/windows/identity/ui/images/main_app.ico
Binary files differ
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 0000000000..e3222e7fdd
--- /dev/null
+++ b/src/windows/identity/ui/images/main_app_old.ico
Binary files differ
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 0000000000..0718d128bf
--- /dev/null
+++ b/src/windows/identity/ui/images/tb-blank-small.bmp
Binary files differ
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 0000000000..01adca9e3d
--- /dev/null
+++ b/src/windows/identity/ui/images/tb-blank.bmp
Binary files differ
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 0000000000..d85cc5cd74
--- /dev/null
+++ b/src/windows/identity/ui/images/tb-space.bmp
Binary files differ
diff --git a/src/windows/identity/ui/images/text1138.png b/src/windows/identity/ui/images/text1138.png
new file mode 100644
index 0000000000..cf5ee376c3
--- /dev/null
+++ b/src/windows/identity/ui/images/text1138.png
Binary files differ
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 0000000000..00adcf4436
--- /dev/null
+++ b/src/windows/identity/ui/images/tk-delete-dis-sm.bmp
Binary files differ
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 0000000000..844fb23a64
--- /dev/null
+++ b/src/windows/identity/ui/images/tk-delete-dis.bmp
Binary files differ
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 0000000000..0ac2d6a8e9
--- /dev/null
+++ b/src/windows/identity/ui/images/tk-delete-sm.bmp
Binary files differ
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 0000000000..501ff6e241
--- /dev/null
+++ b/src/windows/identity/ui/images/tk-delete.bmp
Binary files differ
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 0000000000..9100d9a9c7
--- /dev/null
+++ b/src/windows/identity/ui/images/tk-dis-sm.bmp
Binary files differ
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 0000000000..558ba90644
--- /dev/null
+++ b/src/windows/identity/ui/images/tk-dis.bmp
Binary files differ
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 0000000000..d2e1fcdc4e
--- /dev/null
+++ b/src/windows/identity/ui/images/tk-new-dis-sm.bmp
Binary files differ
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 0000000000..c6abc4ec57
--- /dev/null
+++ b/src/windows/identity/ui/images/tk-new-dis.bmp
Binary files differ
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 0000000000..f2bf279539
--- /dev/null
+++ b/src/windows/identity/ui/images/tk-new-sm.bmp
Binary files differ
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 0000000000..e0e7bc0d65
--- /dev/null
+++ b/src/windows/identity/ui/images/tk-new.bmp
Binary files differ
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 0000000000..2745af1510
--- /dev/null
+++ b/src/windows/identity/ui/images/tk-refresh-dis-sm.bmp
Binary files differ
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 0000000000..45f8099b92
--- /dev/null
+++ b/src/windows/identity/ui/images/tk-refresh-dis.bmp
Binary files differ
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 0000000000..acf33618d2
--- /dev/null
+++ b/src/windows/identity/ui/images/tk-refresh-sm.bmp
Binary files differ
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 0000000000..6e39f87748
--- /dev/null
+++ b/src/windows/identity/ui/images/tk-refresh.bmp
Binary files differ
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 0000000000..c89285c610
--- /dev/null
+++ b/src/windows/identity/ui/images/tk-sm.bmp
Binary files differ
diff --git a/src/windows/identity/ui/images/tk.bmp b/src/windows/identity/ui/images/tk.bmp
new file mode 100644
index 0000000000..e689cb830b
--- /dev/null
+++ b/src/windows/identity/ui/images/tk.bmp
Binary files differ
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 0000000000..aabd10ef57
--- /dev/null
+++ b/src/windows/identity/ui/images/vw-refresh-sm.bmp
Binary files differ
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 0000000000..76b68ddf75
--- /dev/null
+++ b/src/windows/identity/ui/images/vw-refresh.bmp
Binary files differ
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 0000000000..f4d82977ca
--- /dev/null
+++ b/src/windows/identity/ui/images/wdg_collapsed.bmp
Binary files differ
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 0000000000..90ab26d679
--- /dev/null
+++ b/src/windows/identity/ui/images/wdg_collapsed_hi.bmp
Binary files differ
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 0000000000..3bac3fe535
--- /dev/null
+++ b/src/windows/identity/ui/images/wdg_credtype.bmp
Binary files differ
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 0000000000..c590722309
--- /dev/null
+++ b/src/windows/identity/ui/images/wdg_expanded.bmp
Binary files differ
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 0000000000..6b2affc6df
--- /dev/null
+++ b/src/windows/identity/ui/images/wdg_expanded_hi.bmp
Binary files differ
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 0000000000..c563c56e9f
--- /dev/null
+++ b/src/windows/identity/ui/images/wdg_flag.bmp
Binary files differ
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 0000000000..b248bd9d6c
--- /dev/null
+++ b/src/windows/identity/ui/images/wgt_arrow_collapse.ico
Binary files differ
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 0000000000..485b537b03
--- /dev/null
+++ b/src/windows/identity/ui/images/wgt_arrow_expand.ico
Binary files differ
diff --git a/src/windows/identity/ui/khmapp.h b/src/windows/identity/ui/khmapp.h
new file mode 100644
index 0000000000..bd53bde28c
--- /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 0000000000..a1b03b411c
--- /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 0000000000..b92c540b5b
--- /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 0000000000..6115204f3b
--- /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 0000000000..7cd8e01ca9
--- /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 0000000000..0f5c7e0439
--- /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 0000000000..cdaac1e968
--- /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 0000000000..f13a3ec180
--- /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 0000000000..a83325b3a0
--- /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 0000000000..5e83258c4f
--- /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 0000000000..5e83258c4f
--- /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 0000000000..5e83258c4f
--- /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 0000000000..84d91a339e
--- /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 0000000000..84d91a339e
--- /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 0000000000..4b6ce08bf3
--- /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 0000000000..d0b6764221
--- /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 0000000000..0ebdbd4cb3
--- /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 0000000000..bfe9656b89
--- /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 0000000000..4084ede413
--- /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 0000000000..f8c17f68e1
--- /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 0000000000..fe603c0154
--- /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 0000000000..67250c96d9
--- /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 0000000000..b73ec4820b
--- /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 0000000000..13b0ebd2c1
--- /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 0000000000..a58cebb8d3
--- /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 0000000000..75f520c575
--- /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 0000000000..b4f2a6e30f
--- /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 0000000000..bd5b30eff6
--- /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 0000000000..921b9dcc53
--- /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 0000000000..d1a84e235b
--- /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 0000000000..65598debce
--- /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 0000000000..eeb44bbd38
--- /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 0000000000..4e65600936
--- /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 0000000000..80b6ad05f9
--- /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 0000000000..5dc72e6102
--- /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 0000000000..cc383c689b
--- /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 0000000000..14600b0b6f
--- /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 0000000000..e317c7cc54
--- /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 0000000000..69ef01f93c
--- /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 0000000000..5c97c701fa
--- /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 0000000000..37d5e97058
--- /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 0000000000..ccc27b4019
--- /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 0000000000..7b7c22a57b
--- /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 0000000000..d01eb2addc
--- /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 0000000000..c41dddf1fa
--- /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 0000000000..9a96d41f97
--- /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 0000000000..dcbf328ff6
--- /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 0000000000..1742f4a1af
--- /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 0000000000..b772690216
--- /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 0000000000..a5b9d67de3
--- /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 0000000000..96bec88ab8
--- /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 0000000000..f03d2b425f
--- /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 0000000000..d92eb64444
--- /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 0000000000..749aa53bf3
--- /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 0000000000..4d5d5488d9
--- /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 0000000000..57ff309078
--- /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 0000000000..04de09641d
--- /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 0000000000..65fa7aff58
--- /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 0000000000..b9fc80e209
--- /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 0000000000..41f785a952
--- /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 0000000000..179d311f8b
--- /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 &lt; \a key2
+ - \b Positive if \a key1 &gt; \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 0000000000..e9120d6009
--- /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 0000000000..9b4e3800e4
--- /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 0000000000..b50d484daf
--- /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 0000000000..5410d0e522
--- /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 0000000000..2e3b6b7daf
--- /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